Reputation: 99
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
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'
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
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
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
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
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