Frage

Ich „brauchte“ kürzlich eine Zip-Funktion in Perl 5 (während ich darüber nachdachte Wie berechne ich die relative Zeit?), d.h.eine Funktion, die zwei Listen nimmt und sie zu einer Liste „komprimiert“, wobei die Elemente verschachtelt werden.

(Pseudo-)Beispiel:

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

Haskell hat Zip im Prelude Und Perl 6 verfügt über einen Zip-Operator eingebaut, aber wie macht man das in Perl 5 auf elegante Weise?

War es hilfreich?

Lösung

Angenommen, Sie haben genau zwei Listen und sie sind genau gleich lang. Hier ist eine Lösung, die ursprünglich von Merlyn (Randal Schwartz) stammt und sie perversely perlish nennt:

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

Was hier passiert, ist, dass wir für eine Liste mit 10 Elementen zunächst den Drehpunkt in der Mitte finden, in diesem Fall 5, und ihn speichern $p.Dann erstellen wir eine Liste der Indizes bis zu diesem Punkt, in diesem Fall 0 1 2 3 4.Als nächstes verwenden wir map jeden Index mit einem anderen Index zu paaren, der sich im gleichen Abstand vom Drehpunkt befindet wie der erste Index vom Anfang an, was uns (in diesem Fall) 0 5 1 6 2 7 3 8 4 9 ergibt.Dann nehmen wir ein Stück davon @_ Verwenden Sie dies als Liste der Indizes.Das bedeutet, wenn 'a', 'b', 'c', 1, 2, 3 wird übergeben an zip2, wird die Liste neu angeordnet zurückgegeben 'a', 1, 'b', 2, 'c', 3.

Dies kann in einem einzigen Ausdruck nach dem Vorbild von ysth wie folgt geschrieben werden:

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

Ob Sie eine der beiden Varianten verwenden möchten, hängt davon ab, ob Sie sich vorstellen können, sich daran zu erinnern, wie sie funktionieren, aber für mich war es eine Erweiterung des Bewusstseins.

Andere Tipps

Der List::MoreUtils Das Modul verfügt über eine Zip/Mesh-Funktion, die den Zweck erfüllen sollte:

use List::MoreUtils qw(zip);

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

my @zipped = zip @numbers, @fruit;

Hier ist die Quelle der Netzfunktion:

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

    map { my $ix = $_; map $_->[$ix], @_; } 0..$max; 
}

Ich finde die folgende Lösung einfach und leicht zu lesen:

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

Ich glaube, es ist auch schneller als Lösungen, die das Array zuerst in einer falschen Reihenfolge erstellen und dann Slice zum Neuanordnen verwenden, oder Lösungen, die Änderungen vornehmen @a Und @b.

Für Arrays gleicher Länge:

my @zipped = ( @a, @b )[ map { $_, $_ + @a } ( 0 .. $#a ) ];
my @l1 = qw/1 2 3/;
my @l2 = qw/7 8 9/;
my @out; 
push @out, shift @l1, shift @l2 while ( @l1 || @l2 );

Wenn die Listen eine unterschiedliche Länge haben, wird dadurch „undef“ in die zusätzlichen Slots eingefügt, Sie können dies jedoch leicht beheben, wenn Sie dies nicht möchten.Etwas wie ( @l1[0] && shift @l1 ) würde es tun.

Hoffe das hilft!

Algorithm::Loops ist wirklich schön, wenn man so etwas oft macht.

Mein eigener Code:

sub zip { @_[map $_&1 ? $_>>1 : ($_>>1)+($#_>>1), 1..@_] }

Dies ist keineswegs eine elegante Lösung und auch keineswegs die beste Lösung.Aber es macht Spaß!

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

Wie verwendet man STORESIZE/PUSH/POP/SHIFT/UNSHIFT/SPLICE ist eine Übung, die dem Leser überlassen bleibt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top