こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

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

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のコードを書くコードというのも避けたいのです。

文字数を自由に後から設定して、特定の配列から網羅的な組み合わせを生じさせるにはどのようなコードを書けばよいでしょうか?

投稿日時 - 2007-11-11 16:20:36

QNo.3508788

困ってます

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

以下のコードでできると思います。
@resultに入っている文字列の末尾に1文字ずつ足しています。
(最初はforeachを使って書いたけど、途中でmap使った方がすっきりすると気づいた。)
--------------------------------------------------
use strict;

sub str_comb(\@$){
  my @char_list = @{$_[0]}; #第1引数: 構成文字群
  my $length = $_[1]; #第2引数: 文字列長
  my @result = @char_list; #結果を格納する配列

  for(my $i=1; $i<$length; $i++){
    @result = map {
      my $temp=$_;
      map {$temp.$_} @char_list;
    } @result;
  }
  return @result;
}

my @array = qw(A B C D);
my @result = str_comb(@array,5);
print join(",",@result);
--------------------------------------------------


別のやり方でも書いてみたのでそれも載せてみます。
人によってはこっちの方が分かりやすいと感じるかも?
(あともしかしたら少し速いかも。)
--------------------------------------------------
sub str_comb2(\@$){
  my @char_list = @{$_[0]};
  my $length = $_[1];
  my $char_num = @char_list;
  my @result = ();

  for(my $i=0; ; $i++){
    my $i_temp=$i;
    my $str_temp="";
    for(my $j=0; $j<$length; $j++){
      $str_temp .= $char_list[$i_temp % $char_num];
      $i_temp = int($i_temp / $char_num);
    }
    if($i_temp != 0){last;}
    push(@result,$str_temp);
  }
  return @result;
}
--------------------------------------------------

投稿日時 - 2007-11-11 18:12:13

お礼

別の書き方を教えていただき、ありがとうございます。
人によって色々と違いが出てくるものですね。その辺、perlならではなのでしょうか。複数のコードを見比べることでより深く勉強できそうです。

有難うございました。

投稿日時 - 2007-11-11 20:42:40

ANo.2

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.4

> #3
> 最初の方法では, for は
> for my $i (1 .. $length)
> の方が綺麗ではないでしょうか>#2.
たしかに、範囲演算子がある言語ではそっちの方がきれいですね。

C言語がメインなもので、しばらくperlさわってないと
ついfor(;;)のスタイルで書いてしまいます。

投稿日時 - 2007-11-11 19:34:20

ANo.3

最初の方法では, for は
for my $i (1 .. $length)
の方が綺麗ではないでしょうか>#2.

投稿日時 - 2007-11-11 19:16:09

ANo.1

こんちは
ループをまわすたびに
現在の配列のすべての要素に、@arrayの内容をこつこつとくっつけてみました

@array = qw(A B C);
$n = 3;

@mojiretuset = @array;
foreach $i (2..$n) {
@tmp = ();
foreach $elm (@mojiretuset) {
push(@tmp, (map $elm.$_, @array));
}
@mojiretuset = @tmp;
}
print join ",", @mojiretuset;

投稿日時 - 2007-11-11 18:04:29

お礼

有難うございました。
コードを実行したところ、網羅的な文字列が出てきました。
foreach $i (2..$n) {

$temp
を使うあたりが自分では考え付きませんでした。ループの制御というのは難しいですが、同時にとてもおもしろいものですね。

投稿日時 - 2007-11-11 20:40:18

あなたにオススメの質問