如何从 Perl 中的两个不同数组中获取所有可能的组合(不是排列)?
How do I get all possible combinations (not permutations) from two different arrays in Perl?
假设我有这两个数组:
my @varA = ("a","b"); # all possible values of varA
my @varB = (1,2); # all possible values of varB
我想得到输出变量组合:
varA = a, varB = 1; # the first possible combination
varA = a, varB = 2;
varA = b, varB = 1;
varA = b, varB = 2; # the last possible combination
我查看了 Algorithm::Combinatorics
和相关模块,但我只能看到它们用于从 单个 数组中获取元素组合的示例。
有比嵌套循环更简单的方法吗?
编辑:
为了完整起见,我有许多 keys
命名变量,它们可以采用许多可能值之一。我需要生成每个可能的 key=value
对的所有组合(不重复相同的键 - 每个组合只有唯一的键)。
假设我有以下属性键:
ShoeSize # can be one of a set of size values
CurrentAge # can be any integer from 0 up to, say, 110 years
HeightInCm # can be any integer up to, say, 200 (2m tall!)
现在假设我正在研究统计数据,我想生成 ShoeSize、HeightInCm 和 CurrentAge 的每个组合,然后开始计算有多少人匹配每个值组合(尽管忽略计数位 - 这只是为了这个插图 - 我的每个真实组合只有一个实例)。
我将每个组合存储为数组中的“3 键散列”(引用,即每个引用都指向相同匿名散列的实例 set) .
示例输出:
@AoH = (
[ # element 0
{
ShoeSize=10,
CurrentAge=14,
HeightInCm=150
}
],
[ # element 1
{
ShoeSize=12,
CurrentAge=23,
HeightInCm=172
}
],
[ # element 2
{
ShoeSize=8,
CurrentAge=64,
HeightInCm=167
}
],
[ # element 3
{
etc.
}
]
)
如何使用正确的唯一键集生成这个组合哈希数组才是真正的问题。
您似乎要问的是 Cartesian product。
有一些库,比如 Set::CrossProduct and Math::Cartesian::Product.† 第一个提供了一个生成器,可以以多种方式使用,第二个生成一次显示整个结果列表,可以对其进行过滤。
我不清楚您想如何组合两个集合(列表)中的元素 -- 连接值?形成元组?字符串?请澄清,如果需要,我可以添加具体示例。
另请注意,原则上可以只是双 map
或类似
my @cp = map { my $e = $_; map { $_ . $e } @ary2 } @ary1;
(这里的元素是串联的)
编辑 一个完整的示例已添加到问题中,以阐明问题。这是一种方法,使用一个模块来计算三个数组的叉积(三重 map
会有点难看)。
问题中描述的所需输出内置于可用的代码块中。
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd);
use List::Gen qw(cartesian);
my @shoe_size = (8,10); # (apologies to all "other" people...)
my @curr_age = (21, 40);
my @height_cm = (170, 200);
# Returns a generator
my $gen = cartesian {
{ shoe_size => $_[0], curr_age => $_[1], height_cm => $_[2] }
}
\@shoe_size, \@curr_age, \@height_cm;
my @combs = @$gen; # Now this is an array of hashrefs
dd \@combs;
可以使用任何其他方法来形成笛卡尔积,‡ 并从每个组合中构建哈希引用。
上面的代码打印
[
{ curr_age => 21, height_cm => 170, shoe_size => 8 },
{ curr_age => 21, height_cm => 200, shoe_size => 8 },
{ curr_age => 40, height_cm => 170, shoe_size => 8 },
{ curr_age => 40, height_cm => 200, shoe_size => 8 },
{ curr_age => 21, height_cm => 170, shoe_size => 10 },
{ curr_age => 21, height_cm => 200, shoe_size => 10 },
{ curr_age => 40, height_cm => 170, shoe_size => 10 },
{ curr_age => 40, height_cm => 200, shoe_size => 10 },
]
我将 hashrefs 直接放在一个数组中,但如果所需的输出确实需要每个都是 arrayref 的唯一元素,如问题所示,那很容易修改。
打印时hash(ref)元素的顺序可以随意排序。为简单起见,我为每个数据集使用普通数组,然后在输出中硬编码它们的名称;这可以通过适当地安排数据来避免。
一个明确的选择是对所有数据使用哈希,以便每个集合都有一个标签,然后我们可以为其提供排序顺序。这还有另一个好处:我们可以在程序的一个地方按照他们想要的顺序列出名字。让我知道示例是否有用。
† 还有更多。首先,如果您无论如何都可以使用高阶工具,那么一定要查看令人着迷的 List::Gen, which also provides a cartesian function. The module hasn't been touched in a decade but it's all pure Perl so you can use its source 以寻找感兴趣的东西。
‡ 使用其他工具构建产品的示例
和Set::CrossProduct
use Data::Dump qw(dd);
use Set::CrossProduct;
my $cp = Set::CrossProduct->new( { # can give them names
shoe_size => \@shoe_size,
curr_age => \@curr_age,
height_cm => \@height_cm
} );
# Generator ($cp object) is ready
my $combs = $cp->combinations; # all combinations at once
dd $combs;
生成器也可用于一次迭代一个组合。它的生成器有一组精挑细选的小功能。
和Math::Cartesian::Product
use Data::Dump qw(dd);
use Math::Cartesian::Product;
my @cp = map {
{ shoe_size => $_->[0], curr_age => $_->[1], height_cm => $_->[2] }
}
cartesian { 1 } \@shoe_size, \@curr_age, \@height_cm;
dd $_ for @cp;
函数 cartesian
returns 一个带有 arrayrefs 的数组,每个数组都有组合中的元素,所有组合的块 {...}
returns 为真。 (该块只是一个过滤器,但在语法中是强制性的。)
然后通过 map
进行格式化以获得所需的输出。
use Algorithm::Loops qw( NestedLoops );
my $iter = NestedLoops([ \@varA, \@varB ]);
while ( my ($varA, $varB) = $iter->() ) {
say "varA = $varA, varB = $varB;";
}
或
use Algorithm::Loops qw( NestedLoops );
NestedLoops(
[ \@varA, \@varB ],
sub {
my ($varA, $varB) = @_;
say "varA = $varA, varB = $varB;";
}
);
假设我有这两个数组:
my @varA = ("a","b"); # all possible values of varA
my @varB = (1,2); # all possible values of varB
我想得到输出变量组合:
varA = a, varB = 1; # the first possible combination
varA = a, varB = 2;
varA = b, varB = 1;
varA = b, varB = 2; # the last possible combination
我查看了 Algorithm::Combinatorics
和相关模块,但我只能看到它们用于从 单个 数组中获取元素组合的示例。
有比嵌套循环更简单的方法吗?
编辑:
为了完整起见,我有许多 keys
命名变量,它们可以采用许多可能值之一。我需要生成每个可能的 key=value
对的所有组合(不重复相同的键 - 每个组合只有唯一的键)。
假设我有以下属性键:
ShoeSize # can be one of a set of size values
CurrentAge # can be any integer from 0 up to, say, 110 years
HeightInCm # can be any integer up to, say, 200 (2m tall!)
现在假设我正在研究统计数据,我想生成 ShoeSize、HeightInCm 和 CurrentAge 的每个组合,然后开始计算有多少人匹配每个值组合(尽管忽略计数位 - 这只是为了这个插图 - 我的每个真实组合只有一个实例)。
我将每个组合存储为数组中的“3 键散列”(引用,即每个引用都指向相同匿名散列的实例 set) .
示例输出:
@AoH = (
[ # element 0
{
ShoeSize=10,
CurrentAge=14,
HeightInCm=150
}
],
[ # element 1
{
ShoeSize=12,
CurrentAge=23,
HeightInCm=172
}
],
[ # element 2
{
ShoeSize=8,
CurrentAge=64,
HeightInCm=167
}
],
[ # element 3
{
etc.
}
]
)
如何使用正确的唯一键集生成这个组合哈希数组才是真正的问题。
您似乎要问的是 Cartesian product。
有一些库,比如 Set::CrossProduct and Math::Cartesian::Product.† 第一个提供了一个生成器,可以以多种方式使用,第二个生成一次显示整个结果列表,可以对其进行过滤。
我不清楚您想如何组合两个集合(列表)中的元素 -- 连接值?形成元组?字符串?请澄清,如果需要,我可以添加具体示例。
另请注意,原则上可以只是双 map
或类似
my @cp = map { my $e = $_; map { $_ . $e } @ary2 } @ary1;
(这里的元素是串联的)
编辑 一个完整的示例已添加到问题中,以阐明问题。这是一种方法,使用一个模块来计算三个数组的叉积(三重 map
会有点难看)。
问题中描述的所需输出内置于可用的代码块中。
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd);
use List::Gen qw(cartesian);
my @shoe_size = (8,10); # (apologies to all "other" people...)
my @curr_age = (21, 40);
my @height_cm = (170, 200);
# Returns a generator
my $gen = cartesian {
{ shoe_size => $_[0], curr_age => $_[1], height_cm => $_[2] }
}
\@shoe_size, \@curr_age, \@height_cm;
my @combs = @$gen; # Now this is an array of hashrefs
dd \@combs;
可以使用任何其他方法来形成笛卡尔积,‡ 并从每个组合中构建哈希引用。
上面的代码打印
[ { curr_age => 21, height_cm => 170, shoe_size => 8 }, { curr_age => 21, height_cm => 200, shoe_size => 8 }, { curr_age => 40, height_cm => 170, shoe_size => 8 }, { curr_age => 40, height_cm => 200, shoe_size => 8 }, { curr_age => 21, height_cm => 170, shoe_size => 10 }, { curr_age => 21, height_cm => 200, shoe_size => 10 }, { curr_age => 40, height_cm => 170, shoe_size => 10 }, { curr_age => 40, height_cm => 200, shoe_size => 10 }, ]
我将 hashrefs 直接放在一个数组中,但如果所需的输出确实需要每个都是 arrayref 的唯一元素,如问题所示,那很容易修改。
打印时hash(ref)元素的顺序可以随意排序。为简单起见,我为每个数据集使用普通数组,然后在输出中硬编码它们的名称;这可以通过适当地安排数据来避免。
一个明确的选择是对所有数据使用哈希,以便每个集合都有一个标签,然后我们可以为其提供排序顺序。这还有另一个好处:我们可以在程序的一个地方按照他们想要的顺序列出名字。让我知道示例是否有用。
† 还有更多。首先,如果您无论如何都可以使用高阶工具,那么一定要查看令人着迷的 List::Gen, which also provides a cartesian function. The module hasn't been touched in a decade but it's all pure Perl so you can use its source 以寻找感兴趣的东西。
‡ 使用其他工具构建产品的示例
和Set::CrossProduct
use Data::Dump qw(dd);
use Set::CrossProduct;
my $cp = Set::CrossProduct->new( { # can give them names
shoe_size => \@shoe_size,
curr_age => \@curr_age,
height_cm => \@height_cm
} );
# Generator ($cp object) is ready
my $combs = $cp->combinations; # all combinations at once
dd $combs;
生成器也可用于一次迭代一个组合。它的生成器有一组精挑细选的小功能。
和Math::Cartesian::Product
use Data::Dump qw(dd);
use Math::Cartesian::Product;
my @cp = map {
{ shoe_size => $_->[0], curr_age => $_->[1], height_cm => $_->[2] }
}
cartesian { 1 } \@shoe_size, \@curr_age, \@height_cm;
dd $_ for @cp;
函数 cartesian
returns 一个带有 arrayrefs 的数组,每个数组都有组合中的元素,所有组合的块 {...}
returns 为真。 (该块只是一个过滤器,但在语法中是强制性的。)
然后通过 map
进行格式化以获得所需的输出。
use Algorithm::Loops qw( NestedLoops );
my $iter = NestedLoops([ \@varA, \@varB ]);
while ( my ($varA, $varB) = $iter->() ) {
say "varA = $varA, varB = $varB;";
}
或
use Algorithm::Loops qw( NestedLoops );
NestedLoops(
[ \@varA, \@varB ],
sub {
my ($varA, $varB) = @_;
say "varA = $varA, varB = $varB;";
}
);