2

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 {

}

5 Answers 5

3

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.)

Sign up to request clarification or add additional context in comments.

Comments

2

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'

2 Comments

Is there any way without using cpam modules? Can't we do it in standard way?
Why would you need CPAN? $ corelist List::Util -> List::Util was first released with perl v5.7.3
2

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.

Comments

1

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];
   ...
}

Comments

1

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";
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.