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 {
}
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.)
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'
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'
$ corelist List::Util -> List::Util was first released with perl v5.7.3The 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.
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";
}