2012-04-06 52 views
4

我想在Perl中使用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个值(整个运行循环的最低位),但第二次运行失败,因为最后一行的值$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循环可变长度,我不能硬编码每次启动值,所以我在寻找一种方式来最初创建者开始值,然后在循环重置时保留它们。

我意识到这是一个令人困惑的问题,所以请提出问题,我会帮助澄清。

回答

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要在飞行中产生的,我会建议使用partList::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 
    } [email protected] 
    ], 
    \&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"; 
} 
+0

辉煌。我无法正确下载和安装MoreUtils ...我得到了一些关于'undefined symbol:Perl_Istack_sp_ptr'的错误(稍后我会研究它),但是最后一段代码更符合我期待的内容因为 - 我的数组是随机字母,而不是像'a' - 'o'那样。我想如果我能得到它的'一' - 'O',它不会很难适应。非常感谢你的帮助! – incutonez 2012-04-06 11:12:41

+0

实际上,我忽略了你的第一部分代码......这是更优雅的方式。如果我只是在我自己的阵列中进行替换,它可以完美运行。坚实的回答。谢谢。 – incutonez 2012-04-06 11:15:55

+0

刚刚确认MoreUtils也在工作。真的,谢谢。我不认为我可以要求更好的解决方案。 – incutonez 2012-04-06 11:27:43

2

当您使用子例程来生成循环的范围时,每次必须启动一个嵌套循环时才会调用它。这意味着对于包含循环的每次迭代一次。在每次调用前$_设置为包含循环变量的当前值,并且的值全部为包含循环变量作为参数传递。

为了澄清这一点,你已经编写了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"; 
} 
+0

很好的解释。这也适用。谢谢。 – incutonez 2012-04-06 16:06:54