Est-il une élégante fermeture éclair interleave deux listes en Perl 5?

StackOverflow https://stackoverflow.com/questions/38345

  •  09-06-2019
  •  | 
  •  

Question

Récemment, j'ai "besoin" d'un zip en fonction de Perl 5 (tandis que je pensais Comment puis-je calculer le temps relatif?), c'est à direune fonction qui prend deux listes et "zips" ensemble pour une liste, entrelaçant les éléments.

(Pseudo)exemple:

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

Haskell a zip dans le Prélude et Perl 6 dispose d'un zip de l'opérateur construit en, mais comment voulez-vous faire d'une manière élégante en Perl 5?

Était-ce utile?

La solution

En supposant que vous avez exactement deux listes et ils sont exactement de la même longueur, voici une solution à l'origine par merlyn (Randal Schwartz), qui l'a appelé, paradoxalement, perlish:

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

Ce qui se passe ici est que, pour un 10-élément de la liste, en premier lieu, nous trouvons le point de pivot dans le milieu, dans ce cas 5, et de l'enregistrer dans $p.Ensuite, nous faisons une liste d'indices jusqu'à ce point, dans ce cas 0 1 2 3 4.Ensuite, nous utilisons map pour chaque paire d'index avec un autre indice c'est à la même distance à partir du point de pivot comme le premier indice est depuis le début, de nous donner (dans ce cas) 0 5 1 6 2 7 3 8 4 9.Puis nous prenons une tranche de @_ en utilisant ce que la liste des indices.Cela signifie que si 'a', 'b', 'c', 1, 2, 3 est passé à zip2, il sera de retour cette liste réarrangé dans 'a', 1, 'b', 2, 'c', 3.

Cela peut être écrit dans une expression unique, le long de ysth lignes comme ceci:

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

Si vous voulez les utiliser, soit la variation dépend de si vous pouvez voir vous-même de se rappeler comment ils fonctionnent, mais pour moi, c'était un esprit expander.

Autres conseils

L' Liste::MoreUtils module a une fermeture éclair/mesh fonction qui devrait faire l'affaire:

use List::MoreUtils qw(zip);

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

my @zipped = zip @numbers, @fruit;

Voici la source de la maille de la fonction:

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

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

J'ai trouver la solution suivante simple et facile à lire:

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

Je crois que c'est également plus rapide que les solutions que de créer le tableau dans un mauvais ordre d'abord, puis l'utilisation de la tranche pour les réorganiser, les solutions ou les modifier @a et @b.

Pour les tableaux de la même longueur:

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 );

Si les listes sont d'une longueur différente, cela permettra de mettre "undef" dans les slots supplémentaires, mais vous pouvez facilement remédier à cela si vous ne voulez pas le faire.Quelque chose comme ( @l1[0] && shift @l1 ) serait-il le faire.

Espérons que cette aide!

Algorithm::Loops est vraiment gentil si vous faites beaucoup de ce genre de chose.

Mon propre code:

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

Ce n'est absolument pas une solution élégante, ni est-il la meilleure solution par un effort de l'imagination.Mais c'est amusant!

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

Comment gérer STORESIZE/PUSH/POP/SHIFT/UNSHIFT/SPLICE est un exercice laissé au lecteur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top