我想在 Perl 中进行排列。例如,我有三个数组:["big", "tiny", "small"]
然后我还有["red", "yellow", "green"]
and ["apple", "pear", "banana"]
。
如何得到:
[“大”、“红”、“苹果”] [“大”、“红”、“梨”] ..ETC.. [“小”、“绿”、“香蕉”]
我知道这称为排列。但我不知道该怎么做。另外我不知道我可以拥有多少个数组。可能有三四个,所以我不想做嵌套循环。
我想在 Perl 中进行排列。例如,我有三个数组:["big", "tiny", "small"]
然后我还有["red", "yellow", "green"]
and ["apple", "pear", "banana"]
。
如何得到:
[“大”、“红”、“苹果”] [“大”、“红”、“梨”] ..ETC.. [“小”、“绿”、“香蕉”]
我知道这称为排列。但我不知道该怎么做。另外我不知道我可以拥有多少个数组。可能有三四个,所以我不想做嵌套循环。
这实际上不是排列,而是笛卡尔积。请参阅数学::笛卡尔::产品。
#!/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 大红苹果 大红梨 大红香蕉 大黄苹果 大黄梨 大黄香蕉 大青苹果 大绿梨 大绿香蕉 小小的红苹果 小红梨 小红香蕉 小黄苹果 小黄梨 小黄香蕉 小青苹果 小绿梨 小绿香蕉 小红苹果 小红梨 小红香蕉 小黄苹果 小黄梨 小黄香蕉 小青苹果 小绿梨 小绿香蕉
现在以推特形式:
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 ]
} [[]], @_
}
几年前我不得不解决这个确切的问题。我无法想出自己的解决方案,而是遇到了这段精彩的代码,其中涉及巧妙和明智地使用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])
(三个数组引用的列表)到上一层递归。在该级别$last
是对包含的数组的引用[4, 5, 6]
。
然后外部地图的主体运行 3 次,$_
设置为[1]
,然后[2]
,最后[3]
。然后,内部映射(4, 5, 6)
在外部映射的每次迭代中运行,这将返回([1, 4], [1, 5], [1, 6])
,([2, 4], [2, 5], [2, 6])
和 finally ([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 上发布了一个问题,要求有人向我解释这一点。
您可以轻松地使该解决方案适应您的问题。
如果您愿意,可以使用我的Set::CrossProduct模块。您不必遍历整个空间,因为它为您提供了一个迭代器,因此您可以控制。
如果
那么你可以简单地这样做:
对于两个数组@xs
和@ys
:
map{ my $x = $_; map { [$x, $_] } @ys } @xs
对于三个数组@xs
, @ys
,@zs
map{ my $x = $_; map { my $y = $_; map { [$x, $y, $_] } @zs } @ys } @xs
这是我的解决方案,它不需要任何模块,并且可以根据需要使用任意数量的集合。
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"]);