配列の各要素を組み合わせて二次元配列を作る
#!/usr/bin/perl use strict; use warnings; use Data::Dump qw(dump); my @al = qw(1 2 3); my @bl = qw(a b c); my @list; for my $a (@al) { for $b (@bl) { push @list, [$a, $b] } } print dump(@list);
( [1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"], [2, "c"], [3, "a"], [3, "b"], [3, "c"], )
mapでやるとこういう感じ。
my @list = map { my $x = $_; map { [$x, $_] } @bl } @al;
配列の数を3つにしてみる。
my @list; for my $a (@al) { for my $b (@bl) { for my $c (@cl) { push @list, [$a, $b, $c]; } } }
my @list = map { my $x = $_; map { [@$x, $_ ] } @cl } map {my $x = $_; map { [$x, $_] } @bl } @al;
配列の数を増やすたびにforループを深くするわけにもいかないので、再帰を使う。
sub combine { # 一つ目と二つ目の配列を取り出す my ($l1, $l2) = splice @_, 0, 2; my @list; for my $x (@$l1) { for my $y (@$l2) { # 2回目以降は$l1の各要素は配列のリファレンス push @list, [ (ref $x eq "ARRAY") ? @$x : $x, $y]; } } # 引数が空になるまで自分自身を呼び出す (@_) ? combine(\@list, @_) : @list; }
まず、一つ目と二つ目の配列を組み合わせる。次に新しくできた配列と三つ目の配列を組み合わせる。これを繰り返していくと、全ての配列の要素を組み合わせた配列ができる。
サブルーチンを作らずにやるには、reduceを使う。
use List::Util qw(reduce); no warnings qw(once); print reduce { $a + $b } 1..10; #=> 55
reduceは、一つ目の要素を$a、二つ目の要素を$bに代入して、{ }の中の式を評価する。次にその結果を$a、三つ目の要素を$bに代入して、{ }の中の式を評価する。これを配列が空になるまで繰り返す。
no warnings qw(once)は"used only once"という警告が出ないようにするためのもの。$aと$bは一度しか使われていないので、その警告が出る。
#!/usr/bin/perl use strict; use warnings; use Data::Dump qw(dump); use List::Util qw(reduce); my @al = qw(1 2 3); my @bl = qw(a b c); my @cl = qw(A B C); my @list; { no warnings qw(once); $list = reduce { [ map {my $x = $_; map { [(ref $x eq "ARRAY") ? @$x : $x, $_] } @$b } @$a ] } \@al, \@bl, \@cl; } print dump($list);
[ [1, "a", "A"], [1, "a", "B"], [1, "a", "C"], [1, "b", "A"], [1, "b", "B"], [1, "b", "C"], [1, "c", "A"], [1, "c", "B"], [1, "c", "C"], [2, "a", "A"], [2, "a", "B"], [2, "a", "C"], [2, "b", "A"], [2, "b", "B"], [2, "b", "C"], [2, "c", "A"], [2, "c", "B"], [2, "c", "C"], [3, "a", "A"], [3, "a", "B"], [3, "a", "C"], [3, "b", "A"], [3, "b", "B"], [3, "b", "C"], [3, "c", "A"], [3, "c", "B"], [3, "c", "C"], ]