我想在Perl中进行排列。例如,我有三个数组:["big", "tiny", "small"]
然后我有["red", "yellow", "green"]
和["apple", "pear", "banana"]
。
我如何获得:
["big", "red", "apple"] ["big", "red", "pear"] ..etc.. ["small", "green", "banana"]
我理解这称为排列。但我不知道该怎么做。另外我不知道我可以拥有多少阵列。可能有三个或四个,所以我不想做嵌套循环。
答案 0 :(得分:14)
这实际上不是排列,而是Cartesian product。请参阅Math::Cartesian::Product。
#!/usr/bin/perl
use strict; use warnings;
use Math::Cartesian::Product;
cartesian { print "@_\n" }
["big", "tiny", "small"],
["red", "yellow", "green"],
["apple", "pear", "banana"];
输出:
C:\Temp> uu big red apple big red pear big red banana big yellow apple big yellow pear big yellow banana big green apple big green pear big green banana tiny red apple tiny red pear tiny red banana tiny yellow apple tiny yellow pear tiny yellow banana tiny green apple tiny green pear tiny green banana small red apple small red pear small red banana small yellow apple small yellow pear small yellow banana small green apple small green pear small green banana
答案 1 :(得分:6)
几年前我必须解决这个问题。我无法提出自己的解决方案,而是遇到了这段精彩的代码,其中涉及map
以及递归的巧妙和明智的使用:
#!/usr/bin/perl
print "permute:\n";
print "[", join(", ", @$_), "]\n" for permute([1,2,3], [4,5,6], [7,8,9]);
sub permute {
my $last = pop @_;
unless(@_) {
return map([$_], @$last);
}
return map {
my $left = $_;
map([@$left, $_], @$last)
}
permute(@_);
}
是的,这看起来很疯狂,但请允许我解释一下!该函数将递归直到@_
为空,此时它将([1], [2], [3])
(三个arrayrefs的列表)返回到先前的递归级别。在该级别$last
是对包含[4, 5, 6]
的数组的引用。
然后将外部地图的正文运行三次,$_
设置为[1]
,然后[2]
,最后[3]
。然后,对于外部地图的每次迭代,内部地图将在(4, 5, 6)
上运行,这将返回([1, 4], [1, 5], [1, 6])
,([2, 4], [2, 5], [2, 6])
,最后返回([3, 4], [3, 5], [3, 6])
。
最后一个递归调用然后返回([1, 4], [1, 5], [1, 6], [2, 4], [2, 5], [2, 6], [3, 4], [3, 5], [3, 6])
。
然后,它会针对[7,8,9]
运行该结果,从而为您提供[1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 5, 7], [1, 5, 8], [1, 5, 9], [1, 6, 7], [1, 6, 8], [1, 6, 9], [2, 4, 7], [2, 4, 8], [2, 4, 9], [2, 5, 7], [2, 5, 8], [2, 5, 9], [2, 6, 7], [2, 6, 8], [2, 6, 9], [3, 4, 7], [3, 4, 8], [3, 4, 9], [3, 5, 7], [3, 5, 8], [3, 5, 9], [3, 6, 7], [3, 6, 8], [3, 6, 9]
我记得在perlmonks.org上发帖询问有人向我解释这个问题。
您可以轻松地将此解决方案适应您的问题。
答案 2 :(得分:6)
现在以推特形式:
sub prod { reduce { [ map { my $i = $_; map [ @$_, $i ], @$a } @$b ] } [[]], @_ }
use strict;
use warnings;
use List::Util qw(reduce);
sub cartesian_product {
reduce {
[ map {
my $item = $_;
map [ @$_, $item ], @$a
} @$b ]
} [[]], @_
}
答案 3 :(得分:6)
如果您愿意,可以使用我的Set::CrossProduct模块。您不必遍历整个空间,因为它会为您提供迭代器,因此您可以控制它。
答案 4 :(得分:0)
如果强>
然后你可以这样做:
对于两个数组@xs
和@ys
:
map{ my $x = $_; map { [$x, $_] } @ys } @xs
对于三个数组@xs
,@ys
,@zs
map{ my $x = $_; map { my $y = $_; map { [$x, $y, $_] } @zs } @ys } @xs
答案 5 :(得分:0)
这是我的解决方案,它不需要任何模块,并且可以根据需要使用任意数量的集合。
sub set_product {
my @array_of_aref = @_;
if (@array_of_aref == 0) {
return;
}
elsif (@array_of_aref == 1) {
return $array_of_aref[0];
}
elsif (@array_of_aref >= 2) {
my $array_a = shift @array_of_aref;
my $array_b = shift @array_of_aref;
my @array_c;
foreach my $a ($array_a->@*) {
foreach my $b ($array_b->@*) {
if (ref $a eq "" and ref $b eq "") {
push @array_c, [$a, $b];
}
elsif (ref $a eq "ARRAY" and ref $b eq "") {
push @array_c, [$a->@*, $b];
}
elsif (ref $a eq "" and ref $b eq "ARRAY") {
push @array_c, [$a, $b->@*];
}
elsif (ref $a eq "ARRAY" and ref $b eq "ARRAY") {
push @array_c, [$a->@*, $b->@*];
}
}
}
while (my $aref = shift @array_of_aref) {
@array_c = set_product(\@array_c, $aref);
}
return @array_c;
}
}
示例:
print $_->@* foreach set_product(["a","b"]);
print $_->@* foreach set_product(["a","b"], [1,2,3]);
print $_->@* foreach set_product(["a","b"], [1,2,3], ["x","y"]);
print $_->@* foreach set_product(["a","b"], [1,2,3], ["x","y"], ["E","F"]);