jksnw
jksnw

Reputation: 658

Python itertools.product() equivalent in Perl

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

Answers (4)

Sam Choukri
Sam Choukri

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

Sinan &#220;n&#252;r
Sinan &#220;n&#252;r

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

jksnw
jksnw

Reputation: 658

I ended up using:

use Math::Cartesian::Product;
cartesian {print "@_\n"} ["A","B"], ["C", "D", "E"], ["F", "G"];

Upvotes: 3

ikegami
ikegami

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

Related Questions