我正在尝试使用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循环的整个运行),但是第二次运行失败,因为最后一行的$start的值被重置为0……这是一个问题,因为它破坏了一切。
所以我想知道的是,我如何才能基于级别保持$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循环,我不能硬编码每个起始值,所以我正在寻找一种方法来最初创建那些起始值,然后将它们保留下来,以便在循环重置时使用。
我意识到这是一个令人困惑的问题,所以请提出问题,我将帮助澄清。
发布于 2012-04-06 14:36:50
你让这件事变得更难了。
部分问题是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中的part。
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的两个子例程引用修改了相同的变量,并且它们都被多次调用。解决这个问题的最好方法是依赖于子例程被调用时的最后一个值。(从实现上看,这似乎更接近于它的使用方式。)
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-06 19:10:53
使用子例程生成循环范围时,每次必须启动其中一个嵌套循环时都会调用该子例程。这意味着对包含循环的每次迭代都执行一次。在每次调用之前,$_被设置为包含循环的变量的当前值,并且所有包含循环变量的值都作为参数传递。
为了阐明这一点,您编写的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的计算是错误的?在执行上升以重新启动包含循环之前,会对最里面的级别重新计算几次。
由于传递给子例程的参数由包含循环变量的所有值组成,因此可以检查@_的大小,以了解要为哪个级别的循环生成一个范围。例如,在上面的代码中,如果@_包含两个值,分别是$i和$j,则必须返回$k的值;或者,如果只有一个参数,则它是$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";
}https://stackoverflow.com/questions/10038251
复制相似问题