• ベストアンサー

複数の配列の全ての組み合わせを表示する方法

複数の配列が定義されており、その各々から一つづつ要素を選んで出来る組み合わせの全てを表示したいのですが、その際に、foreachを単にネストするのではなく、より効率的方法や関数は何かありませんでしょうか。 たとえば、 @list_a = qw(1 2 3 4) @list_b = qw(a b c d) @list_c = qw(x y z) の3つの配列の全て組み合わせ、例えば、 1ax 1ay 1az 1bx 1by 1bz 1cz ... 4dy 4dzを全て表示させるプログラムを作成したいと思っています。 foreach $a (@list_a){ foreach $b (@list_b){ foreach $c (@list_c){ ... のようにforeachをネストすればよいのですが、配列の数がとても多い場合を考えています。 宜しくお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
  • Werner
  • ベストアンサー率53% (395/735)
回答No.1

foreachは結局3つ使うけど、リストが増えても大丈夫そうではあった。 処理効率については特に考慮してません。 ------------------------------------------------------- my @list_a = qw(1 2 3 4); my @list_b = qw(a b c d); my @list_c = qw(x y z); my @list_d = qw(+ - * /); my @list_all = (\@list_a, \@list_b, \@list_c, \@list_d); my @result = combination(@list_all); print join(" ", @result); sub combination(@){   my @result = @{shift @_};   foreach my $list (@_){     my @temp = ();     foreach my $item_x (@result){       foreach my $item_y (@$list){         push(@temp, $item_x . $item_y);       }     }     @result = @temp;   }   return @result; }

tk_1980024
質問者

お礼

有難うございます。 多次元配列に不慣れで色々調べて、:-(理解できました。 まず@list_aと@list_bの組み合わせを作成し、次に、更にその組み合わせと@list_cの組み合わせを作成し。。。ということを@list_allでループしているのですね。 とても分かりやすかったです。 ところで、多次元配列でもう少し整理する必要があり、それは別の質問をなげようと思いますので、もし良ければそちらも宜しくお願い致します。

その他の回答 (4)

  • aton
  • ベストアンサー率47% (160/334)
回答No.5

頭の体操として考えてみました。 再起呼び出しとシンボリックリファレンスを利用した例です。 動かしてみたので自信はあります。 #&combineの最初の呼び出しの最初の引数は空文字列(シングルクォート二つ)です。 ================================ #!/usr/bin/perl $numVars = 3; @var_1 = qw(1 2 3 4); @var_2 = qw(a b c d); @var_3 = qw(x y z); for ($idx = 0; $idx <= $#var_1; $idx++) { &combine('', 1, $idx); } sub combine { my $outstr = $_[0]; my $depth = $_[1]; my $pos = $_[2]; my $idx = 0; if ($depth <= $numVars) { $outstr = $outstr . ${'var_' . $depth}[$pos]; if ($depth < $numVars) { $depth++; for ($idx = 0; $idx <= $#{'var_' . $depth}; $idx++) { &combine($outstr, $depth, $idx); } } else { print "$outstr\n"; return; } } else { return; } }

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.4

>実際には配列自体の数は10個~20個程度です。 この程度であれば、何とかなりそうです。 考え方としては、 foreach $elm_1 ($list_1){ foreach $elm_2 ($list_2){ foreach $elm_3 ($list_3){ foreach $elm_n ($list_n){ ・・・ }}} のソースを動的に作成し、それを実際に実行します。(evalを呼び出します) 以下のソースを実行して下さい。 ------------------------------ #! /usr/bin/perl # 以下の10個の配列にデータがある。 @list_1 = qw { t01-1 t01-2 t01-3 }; @list_2 = qw { t02-1 t02-2 t02-3 }; @list_3 = qw { t03-1 t03-2 t03-3 }; @list_4 = qw { t04-1 t04-2 t04-3 }; @list_5 = qw { t05-1 t05-2 t05-3 }; @list_6 = qw { t06-1 t06-2 t06-3 }; @list_7 = qw { t07-1 t07-2 t07-3 }; @list_8 = qw { t08-1 t08-2 t08-3 }; @list_9 = qw { t09-1 t09-2 t09-3 }; @list_10 = qw { t10-1 t10-2 t10-3 }; $max_tbl = 3; for ($i = 1; $i <= $max_tbl; $i++){ $str .= " foreach \$elm_${i} (\@list_${i}) { \n "; } $str .= "\$yoso = \"\";"; for ($i = 1; $i <= $max_tbl; $i++){ $str .= "\$yoso .= \$elm_${i};"; $str .= "\$yoso .= ':';"; } $str .= "print \$yoso;"; $str .= "print \"\\n\";"; $str .= "\n"; for ($i = 1; $i <= $max_tbl; $i++){ $str .= "}"; } #print $str,"\n"; eval($str); ------------------------------ eval($str);をコメントアウトして、 #print $str,"\n";のコメントをはずすと、 動的に作成されたスクリプトの内容(=evalで実行される内容)が表示されます。 この場合、$max_tbl = 3;としていますが、ここに、テーブルの数を設定して下さい。

tk_1980024
質問者

お礼

有難うございます。 foreach のループを自動生成させる、という考え方ですね。 直感的で直ぐ使える方法だと思いました。 eval関数を使ったことがなかったのですが、とても参考になりました。

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.3

単に配列のインデックスをインクリメントするものです。 use strict; my @a = qw(1 2 3 4); my $a_offset = 0; my $a_limit = $#a; my @b = qw(a b c d); my $b_offset = 0; my $b_limit = $#b; my @c = qw(x y z); my $c_offset = 0; my $c_limit = $#c; while ($a_offset <= $a_limit) { print "$a[$a_offset]$b[$b_offset]$c[$c_offset] "; if ($c_offset < $c_limit) { ++$c_offset; } elsif ($b_offset < $b_limit) { $c_offset = 0; ++$b_offset; } else { $c_offset = $b_offset = 0; ++$a_offset; } } print "\n";

tk_1980024
質問者

お礼

拝見いたしました。 この方法では各配列の要素数を個別にインクリメントしているので、配列数が沢山あるとその分だけ、インクリメントの制御記述も沢山あることになり、結局はforeachを配列の個数分ネストするのと同じ冗長さがあるような気がしてしまいました。。。 しかし、多少はすっきりするので、こういう手もあるのかと気づきが得られました。 有難うございました。

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.2

>配列の数がとても多い場合 とは、@list_a,@list_b,・・・・と、この配列自体の数がすごく多いと言うことでしょうか。 それとも、3つの配列のなかの、要素の数が、すごく多いのでしょうか。 また、実際問題として、具体的には、どのくらいまでなら、良いのでしょうか。配列自体の数が100万とか1000万とかありますか。 それとも、たかだか1000個以内、程度で良いのでしょうか。

tk_1980024
質問者

補足

回答ありがとうございます。補足です。 > とは、@list_a,@list_b,・・・・と、この配列自体の数がすごく多いと言うことでしょうか。 はい、@list_xの数自体が多いという意味です。要素の数ではありません。 実際には配列自体の数は10個~20個程度です。 以上ですが、いかがでしょうか。

関連するQ&A