Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

List::MoreUtils::zip returns a list of meshed arrays.
%h = zip @k, @v; # ( # k1 => 'v1', # k2 => 'v2', # );
I am looking for something that does the opposite. Example:
unzip {[$_[0], $_[1]]} %h; # ( # [k1 => 'v1'], # [k2 => 'v2'], # );
I do not want iterators (CORE::each, List::MoreUtils::natatime) because then I have to break up my beautiful pipeline of list processing into several statements and flow control blocks. That is stupid and unelegant. There should exist a CORE::map-workalike as shown above that takes a list and processes in a {BLOCK} several items at a time instead of just one item at a time, but I haven't seen it yet.

Replies are listed 'Best First'.
Re: functional opposite of zip
by Tanktalus (Canon) on Mar 03, 2009 at 00:23 UTC

    Something like this?

    #! /usr/bin/perl -l use strict; use warnings; use List::MoreUtils qw(natatime); sub unzip(&$@) { my $c = shift; my $n = shift; my $it = natatime $n, @_; my @rc; while (my @vals = $it->()) { push @rc, $c->(@vals) } @rc; } use Data::Dumper; my %h = qw(k1 v1 k2 v2); print Dumper [ unzip {[$_[0],$_[1]]} 2, @{[%h]} ];
    Though, I have to admit, your syntax is a bit weird. Most of these would use $_, not @_. This would lead to code like this:
    #! /usr/bin/perl -l use strict; use warnings; use List::MoreUtils qw(natatime); sub unzip(&$@) { my $c = shift; my $n = shift; my $it = natatime $n, @_; my @rc; while (my @vals = $it->()) { local $_ = \@vals; push @rc, $c->() } @rc; } use Data::Dumper; my %h = qw(k1 v1 k2 v2); print Dumper [ unzip {[$_->[0],$_->[1]]} 2, @{[%h]} ];
    and I'm not entirely convinced that's better. Either way, of course, we need prototypes to get away without the "sub" keyword, and that necessitates the ugliness in passing in %h. If we got rid of the prototype, we'd have to add sub, and then we could just pass in %h:
    #! /usr/bin/perl -l use strict; use warnings; use List::MoreUtils qw(natatime); sub unzip { my $c = shift; my $n = shift; my $it = natatime $n, @_; my @rc; while (my @vals = $it->()) { local $_ = \@vals; push @rc, $c->() } @rc; } use Data::Dumper; my %h = qw(k1 v1 k2 v2); print Dumper [ unzip sub {[$_->[0],$_->[1]]}, 2, %h ];
    All of these return the same thing, with slightly different syntaxes.

Re: functional opposite of zip
by bellaire (Hermit) on Mar 03, 2009 at 01:51 UTC
    Doesn't seem to be anything that does exactly what you want. Expanding on tanktalus's responses, I offer the following:
    sub unzip(&@) { my $code = shift; my @retlist; while(@_) { local $_ = \@_; push @retlist, $code->(); } return @retlist; } use Data::Dumper; my %h = qw(k1 v1 k2 v2); print Dumper [unzip {[shift @$_,shift @$_]} %h];
    This one will work regardless of the number of items accessed in the code block, because those items are shifted out as the sub works. However, it requires that you refer to the items using shift @$_ rather than your preferred $_[0], etc. Also, this method will preserve the order of a list if invoked on a genuine list rather than a hash.
    sub unzip(&\%) { my $code = shift; my %hash = %{shift()}; my @retlist; while(my @list = each %hash) { push @retlist, $code->(@list); } return @retlist; } use Data::Dumper; my %h = qw(k1 v1 k2 v2); print Dumper [unzip {[$_[0], $_[1]]} %h];
    This one matches your desired syntax exactly, but will only work for hashes, that is the argument %h must be a hash. Also, since it's a hash the order of the result is undefined (as is the standard behavior of each).
Re: functional opposite of zip
by BrowserUk (Patriarch) on Mar 03, 2009 at 02:06 UTC

    Something like this?

    #! perl -slw use strict; sub unzip { my @first; map{ @first ? [ pop( @first ), $_ ] : do{ push( @first, $_ ); () } } @_ } my %h = 'a' .. 'z'; print @$_ for unzip %h; __END__ C:\test>unzip wx ef ab mn st yz uv cd kl qr gh ij op

    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: functional opposite of zip
by roboticus (Chancellor) on Mar 03, 2009 at 11:21 UTC

    Since it looks like you're working with a hash (%h), couldn't you just use keys and values?

    @k = keys %h; @v = values %h;
    ...roboticus