配列の各要素を組み合わせて二次元配列を作る

#!/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"],
]