in reply to Re: map and grep (but for hashes)
in thread map and grep (but for hashes)

Wonderful! But slightly off the specs. Well, perhaps. ;-)

#!perl -l # sub hmap (&%) { my $code = shift; local $_; my @rv; push @rv, shift, $code->($_=shift) while @_; @rv; } sub hgrep (&%) { my $code = shift; local $_; my @rv; push (@rv, ($code->($_=$_[1]) ? (shift,$_):())),shift while @_; @rv; } %h = (foo => 1, bar => 2, baz => 3); %new = hmap { ++$_ } %h; print "hmap result:"; print "$_ => $new{$_}" for keys %new; %new = hgrep { /2/ } %h; print "hgrep result:"; print "found $_ => $new{$_}" for keys %new; __END__ hmap result: bar => 3 baz => 4 foo => 2 hgrep result: bar => 2

update: well, this operates on the value only, localizing $_ and doesn't create package vars. For the key... hm. What do we have? $", $/, $\ ... - no, they could be used inside the block. But then... we could localize @_ here!

sub hmap (&%) { my $code = shift; my @i = @_; local @_; my @rv; push @rv, $code->(@_=(shift @i,shift @i)) while @i; @rv; } %h = (foo => 1, bar => 2, baz => 3); %new = hmap { $_[0]."l",++$_[1] } %h; print "hmap result:"; print "$_ => $new{$_}" for keys %new; __END__ hmap result: bazl => 4 barl => 3 fool => 2

update 2: BrowserUk, as you may have noticed, I ommitted the 'hgrep' part here, since my solution doesn't work - seem to not dunno how to change its arguments inside the block... ;-)

Replies are listed 'Best First'.
Re^3: map and grep (but for hashes)
by BrowserUk (Patriarch) on Jan 30, 2009 at 21:07 UTC
    But slightly off the specs.

    You're missing the " k = something," part of the spec :) How about?

    #! perl -slw use strict; sub hmap(&%) { use vars qw[ $h ]; my $code = shift; my @rv; local( $h, $_ ) = splice( @_, 0, 2 ), push @rv, $h, $code->() while @_; return @rv } sub hgrep (&%) { use vars qw[ $h ]; my $code = shift; my @rv; local( $h, $_ ) = splice( @_, 0, 2 ), push @rv, $code->() ? ( $h, $_ ) : () while @_; return @rv; } my %h = 'a' .. 'z'; print %h; my %r = hmap{ ++$_ } %h; print %r; my %s = hgrep{ $h le 'm' } %r; print %s; __END__ [19:25:50.05] C:\test>hmap wxefabmnstyzuvcdklqrghijop egwyacmosuyaauwcekmgiqsoqik egcekmacgimoik

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      Okay... now here's the next quick shot after some trying. Have to look at the failed attempts. This really a fun challenge, zerohero++ ;-)

      Since $_ is localized for map and grep, it seems the most natural thing to localize @_ for that purpose, and it seems to work... (update: heh, but it hasn't to be localized - perl does that for us)

      #!perl -l # sub hmap (&%) { my $code = shift; @_ % 2 and die "odd number of variables for hmap"; my @i = @_; my @rv; push @rv, $code->(@_=(shift @i,shift @i)) while @i; @rv; } sub hgrep (&%) { my $code = shift; @_ % 2 and die "odd number of variables for hgrep"; my @i = @_; my @rv; for(my $i = 0; $i<= $#_; $i+=2) { local @_ = ($_[$i],$_[$i+1]); $code->(@_) and push @rv, @_; } @rv; } %h = (foo => 1, bar => 2, baz => 3); %new = hmap { $_[0]."l",++$_[1] } %h; print "hmap result:"; print "$_ => $new{$_}" for keys %new; print "original \%h:"; print "$_ => $h{$_}" for keys %h; %new = hgrep { $_[0] =~ /f/ and ($_[0].="l") and $_[1] =~ /1/ and $_[1] += 41; } %h; print "hgrep result:"; print "$_ => $new{$_}" for keys %new; print "original \%h:"; print "$_ => $h{$_}" for keys %h; __END__ hmap result: bazl => 4 barl => 3 fool => 2 original %h: bar => 2 baz => 3 foo => 1 hgrep result: fool => 42 original %h: bar => 2 baz => 3 foo => 1

      No package vars introduced. But it looks like in hgrep localizing @_ is necessary, however in hmap it isn't. Why?

      ;-)
        local @_ = ($_[$i],$_[$i+1]); $code->(@_) and push @rv, @_;

        is the same as

        my @args = ($_[$i],$_[$i+1]); $code->(@args) and push @rv, @args;

        Did you mean the following (which allows elements to be added and removed from @_) or did you repurpose a package variable for nothing?

        local @_ = ($_[$i],$_[$i+1]); &$code and push @rv, @_;

      For the final challenge, develop a new school of Perl Fu around this new potent weapon! If so much is possible with map and grep, think of the new possibilities.

        develop a new school of Perl Fu around this new potent weapon!

        I assumed that you already has uses for them, and I'm not at all convinced about their potency. By flattening the hash to a list, you've effectively thrown away most all the properties that make hashes so useful:

        1. O(1) lookup.
        2. exists

        The most effective uses of hashes involve going directly to the items of interest and avoiding having to iterate the entire structure.

        It's an interesting exercise to construct them, but I think that the uses will be fairly limited. If there were a raft of good uses, someone would have invented them before now.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re^3: map and grep (but for hashes)
by zerohero (Monk) on Jan 30, 2009 at 21:00 UTC

    True, ok, so one refinement would be you'd likely need some way to look at the key, and not just the value. It seems like the way to do this is by using a local variable as in the previous example.