27

私は最近、Perl 5 で zip 関数が「必要」でした (相対時間を計算するにはどうすればよいかを考えていたとき)、つまり、2 つのリストを取り、要素をインターリーブして 1 つのリストにまとめて「圧縮」する関数です。

(疑似) 例:

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

Haskell には Prelude に zip がありPerl 6 には zip オペレーターが組み込まれていますが、Perl 5 でエレガントな方法でそれを行うにはどうすればよいでしょうか?

4

7 に答える 7

37

ちょうど 2 つのリストがあり、それらがまったく同じ長さであると仮定すると、元は merlyn (Randal Schwartz) による解決策です。

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

ここで何が起こるかというと、10 要素のリストの場合、最初に中間点 (この場合は 5) でピボット ポイントを見つけて、それを に保存し$pます。次に、その時点までのインデックスのリストを作成します。この場合は 0 1 2 3 4 です。次に、map各インデックスを、ピボット ポイントから最初のインデックスと同じ距離にある別のインデックスとペアにして、次のようにします。 us (この場合) 0 5 1 6 2 7 3 8 4 9. 次に、それを@_インデックスのリストとして使用してスライスを取得します。これは、'a', 'b', 'c', 1, 2, 3が に渡された場合zip2、そのリストを に再配置して返すことを意味し'a', 1, 'b', 2, 'c', 3ます。

これは、次のように ysth の行に沿って単一の式で記述できます。

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

どちらのバリエーションを使用するかは、それらがどのように機能するかを覚えているかどうかによって異なりますが、私にとっては、それはマインドエキスパンダーでした.

于 2008-09-16T12:56:05.490 に答える
30

List::MoreUtilsモジュールには、このトリックを実行する zip/mesh 関数があります。

use List::MoreUtils qw(zip);

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

my @zipped = zip @numbers, @fruit;

メッシュ関数のソースは次のとおりです。

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

    map { my $ix = $_; map $_->[$ix], @_; } 0..$max; 
}
于 2008-09-01T20:13:58.903 に答える
14

次の解決策は簡単で読みやすいと思います。

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

また、最初に間違った順序で配列を作成してからスライスを使用して並べ替えるソリューションや、@aおよびを変更するソリューションよりも高速だと思います@b

于 2009-02-13T01:37:10.220 に答える
10

同じ長さの配列の場合:

my @zipped = ( @a, @b )[ map { $_, $_ + @a } ( 0 .. $#a ) ];
于 2009-01-28T01:41:56.483 に答える
2
私の@l1 = qw/1 2 3/;
私の @l2 = qw/7 8 9/;
私の@out;
push @out, shift @l1, shift @l2 while ( @l1 || @l2 );

リストの長さが異なる場合、追加のスロットに「undef」が配置されますが、これを行いたくない場合は簡単に修正できます。( @l1[0] && shift @l1 ) のようなものがそれを行います。

お役に立てれば!

于 2008-09-16T09:12:13.220 に答える
2

Algorithm::Loopsこの種のことをたくさんやると本当にいいです。

私自身のコード:

sub zip { @_[map $_&1 ? $_>>1 : ($_>>1)+($#_>>1), 1..@_] }
于 2008-09-19T06:42:19.170 に答える
0

これは完全にエレガントな解決策ではなく、想像力を駆使した最善の解決策でもありません。しかし、それは楽しいです!

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

STORESIZE/ PUSH/ POP///の扱い方は読者に任された演習ですSHIFTUNSHIFTSPLICE

于 2008-10-05T02:47:56.070 に答える