Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl way of iterating over 2 arrays in parallel

Tags:

perl

I used

Use List::MoreUtils qw(each_array);
my $it = each_array( @input_dump_arr, @created_dump_arr);
while ( my ($first, $second) = $it->()) {
}

Would this work in a default perl configuration? A broader question is are there guidelines to write "portable" perl scripts? I'm new to Perl, just trying to figure out if cpan is actually analogous to how boost libraries are to c++.

like image 936
subramanian Avatar asked Apr 15 '13 20:04

subramanian


3 Answers

It's not a core module (meaning it's not bundled with perl), but it's a relatively common one. I wouldn't hesitate to use it.

If you are really paranoid it might not be available, assuming arrays are the same size, you can use something like the following:

for my $i (0 .. $#array1) {
  my $first  = $array1[$i];
  my $second = $array2[$i];
}

However, fortunately each_array() is implemented in pure perl, so you could also view the source of List/MoreUtils.pm and copy and paste the relevant subroutines.

like image 170
Dondi Michael Stroma Avatar answered Nov 07 '22 11:11

Dondi Michael Stroma


You may want to use this simpler rewrite of the module's each_arrayref function. each_array is a superfluous wrapper around this function that uses prototypes to take references to arrays passed as parameters.

Its functionality is identical to the module version, except that it does no checking of the parameters it receives, and the iterator returned doesn't check that it has either no parameters or 'index'.

use strict;
use warnings;

sub each_array {

  my @copy = @_;
  my $i;
  my $max;

  for (map scalar @$_, @copy) {
    $max = $_ unless defined $max and $max > $_;
  }

  sub {
    return $i if @_ and shift eq 'index';
    my $new_i = defined $i ? $i + 1 : 0;
    return if $new_i >= $max;
    $i = $new_i;
    return map $_->[$i], @copy;
  }
}

my @array1 = qw/ A B C /;
my @array2 = qw/ D E F G /;

my $iter = each_array(\@array1, \@array2);

while (my @values = $iter->()) {
  printf "%d: %s\n", $iter->('index'), join ', ', map $_ // 'undef', @values;
}

output

0: A, D
1: B, E
2: C, F
3: undef, G

You could, of course, simply take the code for each_arrayref from the List::MoreUtils module. It is self-contained and will guarantee compatibility with your existing code.

like image 35
Borodin Avatar answered Nov 07 '22 11:11

Borodin


There are modules distributed directly with Perl itself. They're usually called "core modules". You can always use those. List::Util is one of them, though List::MoreUtils is not.

However, List::MoreUtils is a module available on CPAN. CPAN is often considered to be "Perl's standard library", similar to what "the Standard Library" is for C++. Most people think it's OK to require users of their application to have certain modules installed from CPAN, especially if they're well-used modules.

List::MoreUtils is definitely a well-used module. It's been around for quite some time. All major Linux distributions that I know of contain packaged versions of it.

So if your goal is not to be 100% independent of non-core modules then I strongly suggest that you do continue using List::MoreUtils and most of the other fine modules available on CPAN. They're why Perl rocks.

Slightly offtopic: I have to admit that I prefer pairwise (also from List::MoreUtils) for exactly two arrays because it behaves more like map. For your example:

use List::MoreUtils qw(pairwise);
pairwise { do_stuff($a, $b) } @input_dump_arr, @created_dump_arr;
like image 2
Moritz Bunkus Avatar answered Nov 07 '22 12:11

Moritz Bunkus