2008-09-01 9 views
25

私は最近、Perl 5(私は約How do I calculate relative time?を考えていました)のzip関数を "必要"にしました。つまり、2つのリストを1つのリストにまとめて要素をインターリーブする関数です。Perl 5に2つのリストをインターリーブするエレガントなジップがありますか?

(擬似)例:

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

Haskell has zip in the Preludeとに建てPerl 6 has a zip operator、しかし、あなたは、Perl 5のエレガントな方法でそれをどのように行うのですか?

+0

ハスケルのジップはあなたが探しているものではありません。インターリーブされた要素のリストではなく、対応するペアのリストを返します。 –

+0

あなたは正しいです。 Haskellリストには、単一の型の要素が含まれています。ここで私がハスケルに言及したとき、私は考えていませんでした。 – asjo

+2

多くの場合、彼らはジッパーが必要と思うとき、それは2つのリストからハッシュを作成することです。その場合、ハッシュスライスを使用する方がよいでしょう。 '@hash {@keys} = @ values'です。それがここに当てはまらないなら、騒がしいのは残念です。 –

答えて

36

あなたは正確に二つのリストを持っており、彼らは正確に同じ長さであると仮定すると、ここで強情Perl的に呼ばれるメルリンことで、もともと溶液(ランダルシュワルツ)は、あります10要素リストでは、まずピボット点が中央にあります。この場合は5で、$pに保存します。その場合は、その時点までインデックスのリストを作成します。この場合、0 1 2 3 4.次に、mapを使用して、最初のインデックスが開始点からのピボットポイントから同じ距離にある別のインデックスと、 0 5 1 6 2 7 3 8 4 9.その後、インデックスのリストとしてそれを使用して、@_からスライスを取ります。つまり、zip2'a', 'b', 'c', 1, 2, 3が渡された場合、そのリストは'a', 1, 'b', 2, 'c', 3に再配置されます。

これはそうのようなysthのラインに沿って単一の式で書くことができます。

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

あなたは変化のいずれかを使用したいと思いますかどうかは、彼らがどのように機能するかを思い出し、自分自身を見ることができるかどうかに依存しますが、私にとっては、それは心の拡大でした。

+0

うわー、それははっきりと簡潔です! –

+0

+++ 1スマート&ショート – Viet

+0

私の心は吹かれました! – Richard

27

List::MoreUtilsモジュールは、トリックを行う必要がありジップ/メッシュ機能を持っています。ここ

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; 
} 
+0

どのように私はそのモジュールを見落とすことができたのか分かりません - ありがとう! – asjo

+2

エスケープされたアットマークとは何ですか? – dreeves

+1

プロトタイプでは、2〜32個の配列パラメータが必要であり、サブは暗黙的にそれらをarrayrefsとして受け取ると言っています。 – ysth

1
 
my @l1 = qw/1 2 3/; 
my @l2 = qw/7 8 9/; 
my @out; 
push @out, shift @l1, shift @l2 while (@l1 || @l2); 

リストがある場合異なる長さの場合、これは余分なスロットに 'undef'を入れますが、これをしたくない場合は簡単にこれを修正することができます。何か(@ l1 [0] & &シフト@ l1)となります。

希望すると便利です。ここでは何が起こる

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

がためということです。

+1

良い解決策、私はおそらく2つの入力リストを変更しないように私の好みを表明したはずです:-) – asjo

2

Algorithm::Loopsは、この種のことを大事にすると本当にいいです。

私自身のコード:

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

ビットシフトを使用する方がCで高速になるかもしれませんが、Perlでは難読化が必要なだけです。そのように書かれたほうがいいよ。 @_ [地図{$ _、$ _ + @ _/2} 0 .. –

+1

ここでの質問は問題ではありませんが、私のzipは奇数の要素に対しても機能するように設計されています。 – ysth

0

これは完全にエレガントな解決策ではない、またそれは、想像力の任意のストレッチで最高のソリューションです。しかし、それは楽しいです! STORESIZE/PUSH/POP/SHIFT/UNSHIFT/SPLICEを処理する方法

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 

読者に左運動です。同じ長さの配列のために

10

:私は以下のソリューションは単純明快で読みやすい見つける

my @zipped = (@a, @b)[ map { $_, $_ + @a } (0 .. $#a) ]; 
+0

本当に素晴らしい解決策です。それは私のためにそれを理解するのに時間がかかります。 –

+3

これは、不等サイズの配列に問題があります。 –

12

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

私はそれがより速く間違って配列を作成するソリューションよりもだと信じて順序を変更してから、@a@bを変更するソリューションを使用してください。

+1

これは、不等サイズの配列に問題があります。 –

+1

@briandfoyあなたのコメントは助けますが、あなたが忘れたことを知っていますか?不等サイズの配列に対してどちらが*機能するかを指摘する。 (私はこれを行う必要があるもののために買い物をしている) –

+0

私は忘れなかった。その問題は、残りの要素で何をしたいかによって異なります。別の質問をして、制約を指定する必要があります。 –

関連する問題