Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to assign two variables in Perl foreach loop?

Is it possible to assign two variables the same data from an array in a Perl foreach loop?

I am using Perl 5, I think I came across something in Perl 6.

Something like this:

my $var1;
my $var2;

foreach $var1,$var2 (@array){...}
like image 824
johnnylak Avatar asked Apr 12 '15 06:04

johnnylak


People also ask

How does foreach loop work in Perl?

A foreach loop is used to iterate over a list and the variable holds the value of the elements of the list one at a time. It is majorly used when we have a set of data in a list and we want to iterate over the elements of the list instead of iterating over its range.

What is the difference between for and foreach in Perl?

There is no difference. From perldoc perlsyn: The foreach keyword is actually a synonym for the for keyword, so you can use foreach for readability or for for brevity.

Which is faster foreach or while?

There is no major "performance" difference, because the differences are located inside the logic. You use foreach for array iteration, without integers as keys. You use for for array iteration with integers as keys. etc.


2 Answers

It's not in the Perl 5 core language, but List::Util has a pairs function which should be close enough (and a number of other pair... functions which may be more convenient, depending on what you're doing inside the loop):

#!/usr/bin/env perl    

use strict;
use warnings;
use 5.010;

use List::Util 'pairs';

my @list = qw(a 1 b 2 c 3);

for my $pair (pairs @list) {
  my ($first, $second) = @$pair;
  say "$first => $second";
}

Output:

a => 1
b => 2
c => 3
like image 75
Dave Sherohman Avatar answered Oct 07 '22 17:10

Dave Sherohman


The easiest way to use this is with a while loop that calls splice on the first two elements of the array each time,

while (my($var1, $var2) = splice(@array, 0, 2)) {
    ...
}

However, unlike foreach, this continually does a double-shift on the original array, so when you’re done, the array is empty. Also, the variables assigned are copies, not aliases as with foreach.

If you don’t like that, you can use a C-style for loop:

for (my $i = 0; $i < @array; $i += 2) {
     my($var1, $var2) = @array[$i, $i+1];
     ...
}

That leaves the array in place but does not allow you to update it the way foreach does. To do that, you need to address the array directly.

my @pairlist = (
    fee => 1,
    fie => 2,
    foe => 3,
    fum => 4,
);

for (my $i = 0; $i < @pairlist; $i += 2) {
    $pairlist[ $i + 0 ] x= 2;
    $pairlist[ $i + 1 ] *= 2;
}

print "Array is @pairlist\n";

That prints out:

Array is feefee 2 fiefie 4 foefoe 6 fumfum 8

You can get those into aliased variables if you try hard enough, but it’s probably not worth it:

my @kvlist = ( 
    fee => 1,
    fie => 2,
    foe => 3,
    fum => 4,
);

for (my $i = 0; $i < @kvlist; $i += 2) { 
    our  ($key, $value);
    local(*key, $value) = \@kvlist[ $i, $i + 1 ];
    $key   x= 2;
    $value *= 2;
}

print "Array is @kvlist\n";

Which prints out the expected changed array:

Array is feefee 2 fiefie 4 foefoe 6 fumfum 8

Note that the pairs offered by the List::Pairwise module, which were but very recently added to the core List::Util module (and so you probably cannot use it), are still not giving you aliases:

use List::Util 1.29 qw(pairs);

my @pairlist = (
    fee => 1,
    fie => 2,
    foe => 3,
    fum => 4,
);

for my $pref (pairs(@pairlist)) {
    $pref->[0] x= 2;
    $pref->[1] *= 2;
}

print "Array is @pairlist\n";

That prints out only:

Array is fee 1 fie 2 foe 3 fum 4

So it didn’t change the array at all. Oops. :(

Of course, if this were a real hash, you could double the values trivially:

for my $value (values %hash) { $value *= 2 }

The reasons that works is because those are aliases into the actual hash values.

You cannot change the keys, since they’re immutable. However, you can make a new hash that’s an updated copy of the old one easily enough:

my %old_hash = (
    fee => 1,
    fie => 2,
    foe => 3,
    fum => 4,
);

my %new_hash;    
@new_hash{ map { $_ x 2 } keys   %old_hash } = 
           map { $_ * 2 } values %old_hash;

print "Old hash is: ", join(" " => %old_hash), "\n";
print "New hash is: ", join(" " => %new_hash), "\n";

That outputs

Old hash is: foe 3 fee 1 fum 4 fie 2
New hash is: foefoe 6 fiefie 4 fumfum 8 feefee 2
like image 27
tchrist Avatar answered Oct 07 '22 16:10

tchrist