zerohero has asked for the wisdom of the Perl Monks concerning the following question:
Wise monks,
Recently, I asked a question about functions with analogous properties to perl map and grep, only for hashes. I now realize it is useful to change the spec a bit: a map function that operates on hashes and is composable. This would probably be trivial in a language which supported "functional programming", but my belief is you can probably do this in Perl as well.
The following script uses the version of hmap suggested previously by BrowserUK. I learned a lot by taking this little gem apart (it's humbling to me that some people can do so much in so few lines):
use Data::Dumper; my $h = { 1 => { nick => 'rbush', phone => '5551212', bday => '3/12/1965', }, 3 => { nick => 'ernest', phone => '5553300', bday => '3/12/1971', }, 5 => { nick => 'fred', phone => '1112300', bday => '5/01/1972', }, }; sub hmap (&%) { my $code = shift; my @i = @_; local @_; my @rv; push @rv, $code->(@_=(shift @i,shift @i)) while @i; @rv; } my %new = hmap { $_[1]{nick} =~ /rbush|fred/ ? ($_[0], $_[1]) : () } %$h; my %new2 = hmap { $_[1]{nick} =~ /rbush|fred/ ? ($_[0], hmap { $_[0], $_[1] } %{$_[1]}) : () } %$h; print "new = " . Dumper (\%new) . "\n"; print "new2 = " . Dumper (\%new2) . "\n";
Now we run it:
owncloselady-lm:scratch rbush$ perl hmap_ex1.pl new = $VAR1 = { '1' => { 'nick' => 'rbush', 'bday' => '3/12/1965', 'phone' => '5551212' }, '5' => { 'nick' => 'fred', 'bday' => '5/01/1972', 'phone' => '1112300' } }; new2 = $VAR1 = { 'rbush' => 'bday', '1' => 'nick', '3/12/1965' => 'phone', 'nick' => 'fred', 'phone' => '1112300', 'bday' => '5/01/1972', '5551212' => '5' };
Note that we have a multi-level hash (a hash of hashes). Our first example with "%new" works as expected. We are simply selecting each subhash, based on the nickname. The second test with "%new2" is an attempt to put a "do nothing" hmap "inside the loop" so to speak (composition). This is a simple test to see if composition will work. The expectation is I will get the same result as %new, since I'm just copying inputs to outputs.
However, it doesn't preserve the structure. It ends up removing one level of hash structure, and mixing up the keys. It's possible I'm invoking this wrong, or that this is intentional (several perl programmers mentioned they just throw away the structure and flatten things out).
The next script shows my implementation of hmap which allows composition (and preserves structure). The ultimate task is to select records (i.e. with nick = rbush|fred), and then return only the "nick" and "phone" attributes (the next level of the hashes).
use Data::Dumper; my $h = { 1 => { nick => 'rbush', phone => '5551212', bday => '3/12/1965', }, 3 => { nick => 'ernest', phone => '5553300', bday => '3/12/1971', }, 5 => { nick => 'fred', phone => '1112300', bday => '5/01/1972', }, }; sub hmap { my ($h, $code) = @_; return undef unless defined $h; my $rv = {}; while (my ($k, $v) = each %$h) { my $x = &$code($k, $v); $rv->{$k} = $x if (defined $x); } return $rv; } my $new = hmap $h, sub { $_[1]->{nick} =~ /rbush|fred/ ? $_[1] : undef }; my $new2 = hmap $h, sub { return hmap $_[1]->{nick} =~ /rbush|fred/ ? $_[1] : undef, sub { $_[0] =~ /nick|phone/ ? $_[1] : undef } }; print "new = " . Dumper ($new) . "\n"; print "new2 = " . Dumper ($new2) . "\n";
Now we run this version:
owncloselady-lm:scratch rbush$ perl hmap_ex2.pl new = $VAR1 = { '1' => { 'nick' => 'rbush', 'bday' => '3/12/1965', 'phone' => '5551212' }, '5' => { 'nick' => 'fred', 'bday' => '5/01/1972', 'phone' => '1112300' } }; new2 = $VAR1 = { '1' => { 'nick' => 'rbush', 'phone' => '5551212' }, '5' => { 'nick' => 'fred', 'phone' => '1112300' } };
Note the preserved structure for new2. However, it seems there is some clunkiness. For example, I have to use "sub". Any suggestions on how I can make this better? It's not super important that the implementation of hmap be short and elegant, but that the many _uses_ of hmap be short and elegant. Anything that reduces the number of characters you need to type in an expression, increases robustness or flexibility is helpful.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: hmap revisited
by ikegami (Patriarch) on Feb 01, 2009 at 00:13 UTC | |
by zerohero (Monk) on Feb 01, 2009 at 00:49 UTC | |
by ikegami (Patriarch) on Feb 01, 2009 at 05:21 UTC | |
by zerohero (Monk) on Feb 01, 2009 at 19:21 UTC | |
by ikegami (Patriarch) on Feb 01, 2009 at 19:49 UTC | |
|
Re: hmap revisited
by shmem (Chancellor) on Feb 01, 2009 at 20:09 UTC |