4

NestedLoops関数を使用してPerlで順列プログラムを構築しようとしています。これが私のコードです:

use strict;
use warnings;
use Algorithm::Loops qw(NestedLoops);

my @a = 'a'..'o';

my $length = 5;
my $start = 0;
my $depth = 2;

NestedLoops([
  [0..$length],
  ( sub {
    $start = 0 if $start == $depth;
    $start++;
    [$start * $length..$start * $length + $length - 1]
  }) x $depth,
], \&permute,);

sub permute {
  my @ind = @_;
  foreach my $i (@ind) {
    print $a[$i];
  }
  print "\n";
}

したがって、文字「a」から「o」(サイズは15)を保持する配列があります。配列を3行あるかのように扱っているので、配列の想像力は次のようになります。

abcde
fghij
klmno

次に、各ループは各行に対応します...そして私は次のような順列を構築したいと思います:

afk
afl
afm
afn
afo
agk  // fails here... I end up getting agg
...

最初の5つの値(最も低いforループの実行全体)で機能しますが、最後の行の値が$start0にリセットされるため、2番目の実行は失敗します...これはすべてを壊すため問題です。

ですから、私が知りたいのは$start、レベルに基づいて永続性の値を維持するにはどうすればよいかということです...つまり、私が求めているのは、本質的に定数を持つことです。私のループは実際には次のようになります。

for my $a (0..5) {        # 0 at this level and never change
  for my $b (5..10) {     # $start should be 5 at this level and never change
    for my $c (10..15) {  # $start should be 10 at this level and never change
      permute($a, $b, $c);
    }
  }
}

ここで、forループの長さが可変になるため、各開始値をハードコーディングできないため、最初にそれらの開始値を作成し、ループがリセットされたときにそれらを保持する方法を探しています。

これは紛らわしい質問だと思いますので、質問してください。明確にするのを手伝います。

4

2 に答える 2

4

あなたはこれを必要以上に難しくしています。問題の一部は、 NestedLoops
のドキュメントでは、最初の引数のサブルーチン参照がどのように使用されるかについて詳しく説明されていないことです。


次の例では、これがそれらの上のどこかに書かれていると仮定します。

use strict;
use warnings;
use Algorithm::Loops qw'NestedLoops';

NestedLoopsを呼び出して必要なものを取得する最も簡単な方法は、次のとおりです。

NestedLoops(
  [
    ['a'..'e'],
    ['f'..'j'],
    ['k'..'o'],
  ],
  \&permute
);

sub permute {
  print @_, "\n";
}

NestedLoopsの引数をその場で生成したい場合は、 List::MoreUtilsの一部を使用することをお勧めします。

use List::MoreUtils qw'part';

my @a = 'a'..'o';

my $length = 5;
my $index;

NestedLoops(
  [
    part {
      $index++ / $length
    } @a
  ],
  \&permute
);

sub permute {
  print @_, "\n";
}

何らかの理由で配列へのインデックスを使用してNestedLoopsを呼び出したい場合でも、 partを使用すると簡単です。

use List::MoreUtils qw'part';

my @a = 'a'..'o';

my $length = 5;

NestedLoops(
  [
    part {
      $_ / $length
    } 0..@a-1
  ],
  \&permute
);

sub permute {
  print map { $a[$_] } @_;
  print "\n";
}

本当にあなたが抱えている主な問題は、NestedLoopsに与える2つのサブルーチン参照が同じ変数を変更していて、両方とも複数回呼び出されることです。これを修正する最良の方法は、サブルーチンが呼び出されたときにサブルーチンに与えられた最後の値に依存することです。(実装を見ると、これは使用目的に近いようです。)

my @a = 'a'..'o';

my $length = 5;
my $depth = 3;

NestedLoops(
  [
    [0..$length-1],
    (sub{
      return  unless @_;
      my $last = pop;
      my $part = int( $last / $length ) + 1; # current partition
      my $start = $part * $length; # start of this partition
      my $end = $start + $length;
      [$start..$end-1] # list of variables in this partition
    }) x ($depth-1)
  ],
  \&permute
);

sub permute {
  print map { $a[$_] } @_;
  print "\n";
}
于 2012-04-06T06:36:49.873 に答える
2

サブルーチンを使用してループの範囲を生成すると、ネストされたループの1つを開始する必要があるたびに呼び出されます。つまり、包含ループの反復ごとに1回です。各呼び出し$_が包含ループの変数の現在の値に設定される前に、すべての包含ループ変数の値がパラメーターとして渡されます。

これを明確にするために、NestedLoopsコーディングしたステートメントは次のようになります。

sub loop_over {
  $start = 0 if $start == $depth;
  $start++;
  [$start * $length..$start * $length + $length - 1]
};

NestedLoops([
  [0..$length],
  (\&loop_over) x $depth,
], \&permute,);

これは、生のPerlでは、次のようになります。

for my $i (0 .. $length) {

  $_ = $i;
  my $list = loop_over($i);

  for my $j (@$list) {

    $_ = $j;
    my $list = loop_over($i, $j);

    for my $k (@$list) {
      permute($i, $j, $k);
    }
  }
}

計算$startが間違っていることが明らかになったのではないでしょうか。実行が上昇して包含ループを再開する前に、最も内側のレベルで数回再評価されます。

サブルーチンに渡されるパラメーターは、含まれているループ変数のすべての値で構成されているため、のサイズを@_チェックして、範囲を生成するループのレベルを確認できます。たとえば、上記のコードでは、@_2つの値が含まれている場合はと$i$jあるため、の値を$k返す必要があります。または、パラメータが1つしかない場合は、の値で$iあり、戻り値はの範囲である必要があります$j。したがって、の正しい値は、$startの要素の数であり、@_を使用して設定できますmy $start = @_;

このメソッドを使用すると、サブルーチンは最も外側のループの範囲も返すことができます。コードは次のようになります

use strict;
use warnings;

use Algorithm::Loops qw(NestedLoops);

my @a = 'a'..'o';

my $length = 5;
my $start = 0;
my $depth = 2;

NestedLoops([
  (sub {
    $start = @_;
    [$start * $length .. $start * $length + $length - 1];
  }) x ($depth + 1)
], \&permute,);

sub permute {
  print map { $a[$_] } @_;
  print "\n";
}
于 2012-04-06T11:10:52.987 に答える