Reputation: 658
In Python I can use itertools.product()
which the documentation says is the "cartesian product, equivalent to a nested for-loop".
What is it's equivalent in Perl?
An example in Python:
import itertools
opts_list = [["A","B"], ["C","D","E"], ["F","G"]]
print list(itertools.product(*opts_list))
Gives:
[('A', 'C', 'F'), ('A', 'C', 'G'), ('A', 'D', 'F'), ('A', 'D', 'G'), ('A', 'E', 'F'), ('A', 'E', 'G'), ('B', 'C', 'F'), ('B', 'C', 'G'), ('B', 'D', 'F'), ('B', 'D', 'G'), ('B', 'E', 'F'), ('B', 'E', 'G')]
Upvotes: 1
Views: 244
Reputation: 1904
You should use one of the CPAN modules referenced in the other answers. But as a fun exercise I wrote my own function to return the cartesian product for any number of input array references.
my $combinations = get_combinations(["A","B"], ["C","D","E"], ["F","G"]);
foreach my $combo (@$combinations) {
print "@$combo\n";
}
sub get_combinations {
my @arrays = @_;
# pre-determine to the total number of combinations
my $total = 1;
foreach my $aref (@arrays) {
# if our array is empty, add 1 undef item
push(@$aref, undef) unless scalar @$aref;
$total *= scalar @$aref;
}
my $matrix = [];
my $block_size = $total;
for (my $col = 0; $col <= $#arrays; $col++) {
# determine the number of consecutive times to print each item in the column
$block_size = $block_size / scalar @{$arrays[$col]};
# fill-in our 2-D array (matrix), one column (input array) at a time
for (my $row = 0; $row < $total; $row++) {
my $item_index = int($row / $block_size) % scalar @{$arrays[$col]};
$matrix->[$row]->[$col] = $arrays[$col]->[$item_index];
}
}
return wantarray ? @$matrix : $matrix;
}
Upvotes: 0
Reputation: 118148
I like Set::CrossProduct:
#!/usr/bin/env perl
use strict;
use warnings;
use Set::CrossProduct;
my $it = Set::CrossProduct->new([
["A","B"],
["C","D","E"],
["F","G"]
]);
while (my $v = $it->get) {
print "@$v\n";
}
Upvotes: 1
Reputation: 658
I ended up using:
use Math::Cartesian::Product;
cartesian {print "@_\n"} ["A","B"], ["C", "D", "E"], ["F", "G"];
Upvotes: 3
Reputation: 385976
I'd use Algorithm::Loops's NestedLoops
.
use Algorithm::Loops qw( NestedLoops );
my $iter = NestedLoops([["A","B"], ["C","D","E"], ["F","G"]]);
while (my @items = $iter->()) {
...
}
Upvotes: 2