• ベストアンサー

アルファベットn文字の組み合わせ一覧を生成するサブルーチン

「アルファベットを格納した配列」と「文字列長」を引数として与えると、全ての組み合せの文字列を返してくれるサブルーチンを定義するにはどうすればよろしいでしょうか? 「文字列長」が2や3の場合には以下のようにforeach文を入れ子にしていますが、「文字列長」の変化に対応できるようなサブルーチンを定義したいと考えます。 @cc = qw(a b c d); # アルファベットを格納した配列 「文字列長」が2の場合、 foreach my $c1 (@cc){ foreach my $c2 (@cc){ push(@str, $c1.$c2); } } aa ab ac ad ba bb bc bd ca cb cc cd da db dc dd 「文字列長」が3の場合、 foreach my $c1 (@cc){ foreach my $c2 (@cc){ foreach my $c3 (@cc){ push(@str, $c1.$c2.$c3); } } } aaa aab aac aad aba abb abc abd aca acb acc acd ada adb adc add baa bab bac bad bba bbb bbc bbd bca bcb bcc bcd bda bdb bdc bdd caa cab cac cad cba cbb cbc cbd cca ccb ccc ccd cda cdb cdc cdd daa dab dac dad dba dbb dbc dbd dca dcb dcc dcd dda ddb ddc ddd アドバイスをよろしくお願いします。

  • Perl
  • 回答数4
  • ありがとう数2

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

  • ベストアンサー
  • kabaokaba
  • ベストアンサー率51% (724/1416)
回答No.2

No.1です. 問題を誤読してました. 順列ではないですね. 問題は,文字が``N''種類あるときに n桁の文字列を生成するということでした. N進数のn桁の数をすべて列挙するのと同じなので 例えば以下のようにできます. use strict; use warnings; use strict; use warnings; sub to_N_pos{ my ($n,$N,$L)=@_; my @result; for my $i (1..$L){ unshift @result, $n % $N; $n = int($n / $N); } return $n ? () : @result; } sub listup{ my ($list,$L)=@_; my @result; my $N=scalar @{$list}; my $n=0; while(my @r = to_N_pos($n,$N,$L)){ my $result; for my $i (@r){ $result = $result.$list->[$i]; } push @result, $result; $n++; } return @result; } $,=", "; print listup(['a'..'d'],2); listupは 文字のリストのリファレンス,文字列の長さ を引数として, 結果を配列として出力します.

suzokwave
質問者

お礼

迅速なアドバイスをいただき心より感謝申し上げます。

その他の回答 (3)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

試していないので無保証ですが, 再帰を使わずがんばってみる: sub allstring(\@$) { my ($alphabet, $count) = @_; my @ans = (); for my $i (1 .. $count) { my @tmp = (); for my $ch (@$alphabet) { push @tmp, map { $_ . $ch } @ans; } @ans = @tmp; } @ans; } 例: $, = ','; print allstring(['a' .. 'd'], 2), "\n";

suzokwave
質問者

補足

回答ありがとうございました。以下のエラーが出力されました Type of arg 1 to main::allstring must be array (not single ref constructor) at Tacosan.pl line 15, near "2)" Execution of Tacosan.pl aborted due to compilation errors.

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

再帰呼び出しを使った方法です。 use strict; my @cc = qw(a b c d); my $n = 3; # 文字列長を指定 my @result; my @work = (); search(@cc); sub search { my @list = @_; foreach my $item (@list) { if (@work == ($n - 1)) { push @result, join('', @work, $item); } else { push @work, $item; search(@list); pop @work; } } } print "@result\n";

suzokwave
質問者

お礼

再帰呼び出しを使った方法は大変勉強になりました。ありがとうございました。

  • kabaokaba
  • ベストアンサー率51% (724/1416)
回答No.1

http://faq.perl.org/perlfaq4.html ↑で How do I permute N elements of a list? という項目を見ましょう. MJDの``Higher-Order Perl''という書籍では 4.3.1節にかなり詳細にでています. コードそのものは http://hop.perl.plover.com/Examples/ の``permuteなんちゃらら''というものです. 何種類かあります. 一般的にやるとかなり複雑なコードになります.

関連するQ&A

  • 配列から網羅的な文字列を生成するには?

    perlの配列を使った、網羅的な文字列生成について質問です。 ある特定の種類の文字のレパートリをつかった、n文字の文字列すべての組み合わせを生成したいと考えています。 例えば文字 A, B, C の三種類をつかった2文字の文字列なら AA,AB,AC,BA,BB,BC,CA,CB,CC 3x3 =9 種類というふうになります。 n=2の場合、 @array = qw(A B C); foreach $moji(@array){ $moji1 = $moji; foreach $moji(@array){ $moji2 = $moji; $mojiretu = $moji1.$moji2; push (@mojiretuset , $mojiretu ); }} print @mojiretuset; とするとforeachをふたつ重ねることで文字の組み合わせすべてを生成できました。 問題なのは、問題なのは、今私がしたいのは文字数nを(ループの)外から指定して(例えば$mojisuu = 6 などとして)n文字の場合の網羅的な文字の組み合わせを生じさせることなのです。 毎回自分でforeachを必要なだけ重ねて書き直す、というのは現実的ではありませんし、n個のforeachの入ったperlのコードを書くコードというのも避けたいのです。 文字数を自由に後から設定して、特定の配列から網羅的な組み合わせを生じさせるにはどのようなコードを書けばよいでしょうか?

    • ベストアンサー
    • Perl
  • cshで、文字をアルファベット順に並べかえたい

    cshellで配列に格納した文字を、アルファベット順に並べ替えたいのですが、いい方法はないでしょうか? array[1] = 'T'; array[2] = 'B'; array[3] = 'A'; のように文字の格納された配列があるのですが、 これを、アルファベット順に、 array[1] = 'A'; array[2] = 'B'; array[3] = 'T' このように並べ替えたいのです。 printfを使って、文字コードを比較し、並べ替えようとしたのですが、cshでは、エラーになってしまいます。 また、LINUXのsortコマンドを使う方法もためしてみたのですが、cshスクリプト内で、コマンドが実行されません。 別の方法があるのでしょうか?それとも上記の2つの方法で実装できるのでしょうか? どうか皆さまの知恵を貸して下さい。 よろしくお願い致します。

  • サブルーチンの使い方

    以下のプログラムを解読中ですが、いまいち理解できません。C言語の経験はあるのですが、Perl初心者のため、Perlの文法を中心に、以下のポイントについて教えてください。 (1)この関数で、引数が「&@」となっていますが、この&の意味は「サブルーチン」ということですか?? (2)2行目のmy ($block, @listin) = @_ で各$block, @listinに格納される値を教えてください。「@_」を調べてみたところ、「サブルーチンの引数を代入」とあるのですが、ここでは、map_block(&@)の&@が&blockと@litinに代入されるということですか??そもそも&@とは何でしょうか? (3)4行目のpush @listout, &$block() for (@listin) について、push関数を調べてみたところ、配列の最後尾に値を格納するということですが、&$block() for (@listin) の部分の意味がよくわかりません。どのような値が、配列listoutに格納されるのでしょうか? (解読中のプログラム) 1sub map_block (&@) { 2   my ($block, @listin) = @_; 3 my @listout = (); 4 push @listout, &$block() for (@listin); 5 return @listout; 6}

    • ベストアンサー
    • Perl
  • Perlのサブルーチンで連想配列(ハッシュ)を渡す

    Perlのサブルーチンで連想配列(ハッシュ)を渡す方法 -------------------------------------- $str=rep::replace(%A,$B,@C); -------------------------------------- sub refidrep ( \%\$\@ ){ my(%A,$B,@C)=(@_); } -------------------------------------- では、上手くいきませんでした。 どのようにしたら、連想配列をサブルーチンに渡せるのでしょうか? 分かる方いらっしゃいましたらご教授ください。

    • ベストアンサー
    • Perl
  • 以前、ご質問させていただき、ご回答をいただいたのですが、

    以前、ご質問させていただき、ご回答をいただいたのですが、 'server0[123]', 'client0[1-35-7]', 'client0[13-69]', 'client[1-3][0-9]' の文字判定をお聞きし、自分なりにサブルーチン化して対応いたしましたが、 このたび 'server0[1-3]0','server0[1-3]2','client0[a-z1]',client[a-c][12] 等の文字列も判定する必要がでてきました。 sub host{ my @reg_str = @_; my (@work, @result); foreach my $reg_str (@reg_str) { if ($reg_str = ~/\[/ ){ my $head = substr($reg_str, 0, index($reg_str, '[')); my @num = map { s/(\d)-(\d)/join('', $1 .. $2)/eg; [split //] } $reg_str =~ /\[([\d-]+)\]/g; @ref_result = (); @ref_result = comb(@num); print join(',', map { $head . $_ } @result), "\n\n"; }else{ print "$reg_str\n"; } sub comb { my $ref = shift; @result = (); foreach my $i (@$ref) { push @work, $i; unless (@_) { push @result, join('', @work); } else { comb(@_); } pop @work; } return (@result); } } 'server0[123]', 'client0[1-35-7]', 'client0[13-69]' の分解は可能なのですが、 'client[1-3][0-9]' は client10,client11,client12・・・・・・client37,client38,client39 となってほしいのですが、 client31,client32・・・・・,client38,client39となってしまいます。 また括弧の後ろに括弧無しがきた場合は、うまく展開できません。 お忙しいところ、誠にくだらない質問かもしれませんが、お力をお借りできれば幸いです。 よろしくお願いします。

    • ベストアンサー
    • Perl
  • javaプログラミングでのかぶった文字列の除去

    javaを勉強していて、考えると頭がこんがらがってしまったので 質問します。アドバイスお願いします。 今、例えば 配列にstr[0]に"本"、str[1]に"部品"、str[2]に"本"、str[3]に"部品"、str[4]に"交換" という文字列が格納されているとして、 新しく作った配列str2に配列strでかぶっている文字列を取り除いて 格納するにはどのようにすればよいのでしょうか? str2[0]に"本"、str2[1]に"部品"、str2[2]に"交換"という風に格納したいです。 使えるメソッドなどありましたら教えてください。 よろしくお願いします。

    • ベストアンサー
    • Java
  • 課題で、アルファベットを入力するとすべてのアルファベットを小文字に変換

    課題で、アルファベットを入力するとすべてのアルファベットを小文字に変換して出力するプログラムを作成しました。2度以上同じ処理を行う場合はその部分を関数としなければいけないのですが、うまくできなかったようです。一応実行結果のとおりに表示されますが、複数のエラーや警告が出ました。どこが間違っているのでしょうか? ・警告 W8065 test.c 9: プロトタイプ宣言のない関数 'henkan' の呼び出し(関数 main ) ・エラー E2379 test.c 10: ステートメントにセミコロン(;)がない(関数 main ) ・警告 W8070 test.c 10: 関数は値を返すべき(関数 main ) ・エラー E2356 test.c 12: 'henkan' の再宣言で型が一致していない ・エラー E2344 test.c 5: 一つ前の 'henkan' の定義位置 ・エラー E2040 test.c 16: 宣言が正しく終了していない ・エラー E2141 test.c 16: 宣言の構文エラー ・エラー E2141 test.c 16: 宣言の構文エラー ・エラー E2190 test.c 29: 不要な } 実行結果↓ **************************** Atsushi TAKEDA ==>atsushi takeda **************************** #include <stdio.h> #include <string.h> #include <ctype.h> int henkan(); int main(void) { henkan() } int henkan; char str[100]; int i,j,chk,len; for(j=0;j<6;j++){ gets(str); len=strlen(str); printf("==>"); for(i=0;i<len;i++){ chk=isupper(str[i]); if(chk!=0) str[i]=tolower(str[i]); printf("%c",str[i]); } printf("\n"); } return 0; }

  • 2次元配列をポインタで参照したいのですが・・

    下記Aのような2次元配列を作るとします。 A:1行あたり1024文字(ASCII)格納できるN行の2次元配列 Aの定義をまず下記Bのように行い、その後サイズ分メモリを確保しました。 B:char *str; この変数strを使って、x番目の行のy番目の文字を参照したい場合、どのようにすれば良いのでしょうか? 下記Cではやはりうまくいかないのでしょうか? C:str[x][y] 申し訳ございませんが、ご教授よろしくお願い致します。

  • 任意の文字の組み合わせ

    Math::BaseCalc モジュールを使って 10 進数をアルファベットに変換すると、 $ perl -MMath::BaseCalc -le 'print Math::BaseCalc -> new(digits => ['A' .. 'Z']) -> to_base(26);' BA このように 26 -> BA になりますが、これが 26 -> AA となるようなモジュールはないのでしょうか? 0 (A) == 00 (AA) なので、基数変換ならば 26 -> BA で正しいのは分かります。 しかし、文字列的な変換方法を探しています。 別な言い方をすると、A-Z の組み合わせでできる文字列を順番に得たいと思っています。 すなわち、A, B, C, ..., Z, AA, AB, AC, ..., ZZ, AAA のような文字列です。 アルファベットならば範囲演算子 (..) を使って得られますが、任意の digits で同じような文字列を得ることができるようなモジュールはないのでしょうか? (例えば、0-9 A-Z a-z ならば 0, 1, 2, ..., 9, A, B, C, ..., Z, a, b, c, ..., z, 00, 01, 02, ...) 現在は拙作のサブルーチンを使っているのですが、もし定番のモジュールがありましたら、お教え頂きたいです。 ご回答よろしくお願い申し上げます。

    • ベストアンサー
    • Perl
  • アルファベットを入力するとすべてのアルファベットを小文字に変換して出力

    アルファベットを入力するとすべてのアルファベットを小文字に変換して出力するプログラムを作成しました。2度以上同じ処理を行う場合はその部分を関数としなければいけないのですが、実行結果のように出力できませんでした。。どこが間違っているのでしょうか? 実行結果↓ ************ AOmori ==>aomori iwate ==>iwate AKITA ==>akita ************ #include <stdio.h> #include <string.h> #include <ctype.h> int henkan(void); int main(void) { char str[100]; int j,len; for(j=0;j<6;j++){ gets(str); printf("==>"); } henkan(); return 0; } int henkan(void) { char str[100]; int i, chk,len; for(i=0;i<len;i++){ chk=isupper(str[i]); if(chk!=0)str[i]=tolower(str[i]); { printf("%c",str[i]); } } printf("\n"); return chk; }

専門家に質問してみよう