Crazy coder
Crazy coder

Reputation: 99

syntax for multiple array in a single foreach loop in perl

I need to know the proper syntax for multiple arrays in a single foreach loop.

In TCL, we can have multiple list in a single foreach loop. But how to do it with perl?

#TCL eg:
foreach i $a j $e {

}

Upvotes: 2

Views: 1207

Answers (5)

Stefan Becker
Stefan Becker

Reputation: 5962

The TCL syntax isn't clear, so I went to the source, i.e. tclsh:

% set a { 1 2 3 4 }
 1 2 3 4
% set e { 4 5 6 7 0 9 }        
 4 5 6 7 8 9
% foreach i $a j $e { puts "'$i' '$j'" }      
'1' '4'
'2' '5'
'3' '6'
'4' '7'
'' '0'
'' '9'

TCL foreach:

Values in each list are used in order from first to last, and each value is used exactly once. The total number of loop iterations is large enough to use up all the values from all the value lists. If a value list does not contain enough elements for each of its loop variables in each iteration, empty values are used for the missing elements.

In Perl "empty" elements are undef, so I guess the closest translation to Perl would be:

#!/usr/bin/perl
use strict;
use warnings;

use List::Util qw(max);

my @a = (1, 2, 3, 4);
my @e = (4, 5, 6, 7, 0, 9);
my $max_index = max($#a, $#e);

foreach my $index (0..$max_index) {
    my $i = $a[$index] // '';
    my $j = $e[$index] // '';

    # your "TCL" loop code goes here...
    print "$index '$i' '$j'\n";
}

exit 0;

Test output:

$ perl dummy.pl
0 '1' '4'
1 '2' '5'
2 '3' '6'
3 '4' '7'
4 '' '0'
5 '' '9'

Upvotes: 2

Grinnz
Grinnz

Reputation: 9231

The List::UtilsBy function zip_by may be useful for this task. Unfortunately there is no good way to declare multiple iteration variables in a foreach loop currently, so we use arrayrefs.

use strict;
use warnings;
use List::UtilsBy 'zip_by';
my @arr1 = ...;
my @arr2 = ...;
foreach my $zipped (zip_by { [@_] } \@arr1, \@arr2) {
  my ($i, $j) = @$zipped;
  ...
}

The List::SomeUtils zip function does this same task more concisely, but has an awkward prototype requiring arrays, where zip_by is more flexible.

Upvotes: 2

Dave Sherohman
Dave Sherohman

Reputation: 46187

Current versions of the List::MoreUtils module provide a zip_unflatten command which will combine two (or more) arrays into a single array containing references to an array of the first element of each list, then the second, and so on:

#!/usr/bin/env perl    

use strict;
use warnings;
use 5.010;

use List::MoreUtils 'zip_unflatten';

my @a = 1 .. 4;
my @e = 5 .. 9;

my @z = zip_unflatten(@a, @e);

for my $pair (@z) {
  my $i = $pair->[0] // '-';
  my $j = $pair->[1] // '-';

  say "$i\t$j";
}

(The // operator I use here is "defined-OR", which is basically the same as ||, except it works on whether the variable to its left has a defined value rather than whether it has a true value.)

Upvotes: 3

Dave Sherohman
Dave Sherohman

Reputation: 46187

With just core Perl, there's also the option of using shift, but note that this method modifies (empties) the arrays, so use copies of them if it matters:

#!/usr/bin/env perl    

use strict;
use warnings;
use 5.010;

my @a = 1 .. 4;
my @e = 5 .. 9;

while (@a || @e) {
  my $i = shift @a // '-';
  my $j = shift @e // '-';

  say "$i\t$j";
}

Upvotes: 1

ikegami
ikegami

Reputation: 385655

To someone who doesn't know TCL, it's not clear what you want, but I think you want the following:

for my $i (0..$#foos) {
   my $foo = $foos[$i];
   my $bar = $bars[$i];
   ...
}

Upvotes: 1

Related Questions