Czy istnieje elegancki zip do przeplatania dwóch list w Perlu 5?

Ostatnio "potrzebowałem" funkcji zip w Perlu 5 (gdy myślałem o Jak obliczyć czas względny?), tzn. funkcja, która pobiera dwie listy i" zamyka " je razem w jedną listę, przeplatając elementy.

(Pseudo)przykład:

@a=(1, 2, 3);
@b=('apple', 'orange', 'grape');
zip @a, @b; # (1, 'apple', 2, 'orange', 3, 'grape');

Haskell ma zip w preludium i Perl 6 ma wbudowany operator zip, ale jak to zrobić w elegancki sposób w Perlu 5?

 26
Author: Community, 2008-09-01

7 answers

Zakładając, że masz dokładnie dwie listy i są one dokładnie tej samej długości, oto rozwiązanie pierwotnie merlyn (Randal Schwartz), który nazwał je przewrotnie perlish:

sub zip2 {
    my $p = @_ / 2; 
    return @_[ map { $_, $_ + $p } 0 .. $p - 1 ];
}

Dzieje się tak, że dla listy 10-elementowej najpierw znajdujemy punkt obrotu w środku, w tym przypadku 5, i zapisujemy go w $p. Następnie sporządzamy listę indeksów do tego momentu, w tym przypadku 0 1 2 3 4. Następnie używamy map, Aby sparować każdy indeks z innym indeksem, który znajduje się w tej samej odległości od obrotu punkt, ponieważ pierwszy indeks jest od początku, dając nam (w tym przypadku) 0 5 1 6 2 7 3 8 4 9. Następnie bierzemy kawałek z @_ używając go jako listy indeksów. Oznacza to, że jeśli 'a', 'b', 'c', 1, 2, 3 zostanie przekazana do zip2, zwróci listę przestawioną na 'a', 1, 'b', 2, 'c', 3.

Można to zapisać w jednym wyrażeniu wzdłuż linii ysth w następujący sposób:

sub zip2 { @_[map { $_, $_ + @_/2 } 0..(@_/2 - 1)] }

To, czy chcesz użyć jednej z odmian, zależy od tego, czy widzisz siebie, pamiętając, jak działają, ale dla mnie był to umysł expander.

 36
Author: Aristotle Pagaltzis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-21 21:49:19

Moduł List:: MoreUtils posiada funkcję zip / mesh, która powinna zadziałać:

use List::MoreUtils qw(zip);

my @numbers = (1, 2, 3);
my @fruit = ('apple', 'orange', 'grape');

my @zipped = zip @numbers, @fruit;

Oto źródło funkcji siatki:

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) {
    my $max = -1;
    $max < $#$_  &&  ($max = $#$_)  for @_;

    map { my $ix = $_; map $_->[$ix], @_; } 0..$max; 
}
 28
Author: Jason Navarrete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-02 20:29:26

Uważam, że następujące rozwiązanie jest proste i łatwe do odczytania:

@a = (1, 2, 3);
@b = ('apple', 'orange', 'grape');
@zipped = map {($a[$_], $b[$_])} (0 .. $#a);

Uważam, że jest to również szybsze niż rozwiązania, które najpierw tworzą tablicę w złej kolejności, a następnie używają slice do zmiany kolejności, lub rozwiązania, które modyfikują @a i @b.

 13
Author: Frank,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-13 01:47:42

Dla tablic o tej samej długości:

my @zipped = ( @a, @b )[ map { $_, $_ + @a } ( 0 .. $#a ) ];
 10
Author: jmcnamara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-27 23:53:04

Algorithm::Loops to naprawdę miłe, jeśli robisz wiele takich rzeczy.

Mój własny kod:

sub zip { @_[map $_&1 ? $_>>1 : ($_>>1)+($#_>>1), 1..@_] }
 2
Author: ysth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-14 19:47:57
my @l1 = qw/1 2 3/;
my @l2 = qw/7 8 9/;
my @out; 
push @out, shift @l1, shift @l2 while ( @l1 || @l2 );

Jeśli listy mają inną długość, spowoduje to dodanie 'undef' do dodatkowych slotów, ale możesz to łatwo naprawić, jeśli nie chcesz tego robić. Coś w stylu ( @l1[0] && shift @l1 ) zrobiłoby to.

Mam nadzieję, że to pomoże!
 1
Author: jonfm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 09:12:13

To nie jest eleganckie rozwiązanie, ani nie jest najlepszym rozwiązaniem dla jakiegokolwiek odcinka wyobraźni. Ale to jest zabawne!

package zip;

sub TIEARRAY {
    my ($class, @self) = @_;
    bless \@self, $class;
}

sub FETCH {
    my ($self, $index) = @_;
    $self->[$index % @$self][$index / @$self];
}

sub STORE {
    my ($self, $index, $value) = @_;
    $self->[$index % @$self][$index / @$self] = $value;
}

sub FETCHSIZE {
    my ($self) = @_;
    my $size = 0;
    @$_ > $size and $size = @$_ for @$self;
    $size * @$self;
}

sub CLEAR {
    my ($self) = @_;
    @$_ = () for @$self;
}

package main;

my @a = qw(a b c d e f g);
my @b = 1 .. 7;

tie my @c, zip => \@a, \@b;

print "@c\n";  # ==> a 1 b 2 c 3 d 4 e 5 f 6 g 7

Jak sobie radzić STORESIZE/PUSH/POP/SHIFT/UNSHIFT/SPLICE jest ćwiczeniem pozostawionym czytelnikowi.

 0
Author: ephemient,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-05 02:47:56