foreach my $var (@list) -- $var is a reference?
Asked Answered
U

6

10

So, I never knew this and I want to get some clarifcation on it. I know if you do

foreach (@list){

if you change $_ in that loop it will affect the actual data. But, I did not know that if you did

foreach my $var1 (@list){

If you changed $var1 in the loop it would change the actual data. :-/ So, is there a way to loop over @list but keep the variable a read-only copy, or a copy that if changed will not change the value in @list?

Unhand answered 18/6, 2009 at 15:15 Comment(0)
P
10

$var is aliased to each item in turn.

See http://perldoc.perl.org/perlsyn.html#Foreach-Loops

If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over.

Purport answered 18/6, 2009 at 15:22 Comment(0)
Q
10

Easiest way is just to copy it:

foreach my $var1 (@list) {
    my $var1_scratch = $var1;

or

foreach my $var1 ( map $_, @list ) {

But if $var1 is a reference, $var1_scratch will be a reference to the same thing. To be really safe, you'd have to use something like Storable::dclone to do a deep copy:

foreach my $var1 ( @{ Storable::dclone( \@list ) } ) {
}

(untested). Then you should be able to safely change $var1. But it could be expensive if @list is a big datastructure.

Quenna answered 18/6, 2009 at 15:21 Comment(2)
Be careful, $var1 is not a reference, but an alias. See the difference : my $var2=\$list[0]; print "$var2\n"; displays SCALAR(0x8171880)Peraea
@wazoox: He meant if $var1 contained a reference, e.g. if @list contained a bunch of HASH refs. Even if you break the aliasing, you still have another copy of a reference to the same thing.Patrizia
P
10

$var is aliased to each item in turn.

See http://perldoc.perl.org/perlsyn.html#Foreach-Loops

If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over.

Purport answered 18/6, 2009 at 15:22 Comment(0)
W
4

It is an alias, not a reference. If you want to create your own aliases (outside of for) you can use Data::Alias.

Webworm answered 18/6, 2009 at 15:51 Comment(2)
Umm... +0. That's +1 for the alias/reference clarification and -1 for giving instructions on doing the opposite of what the OP asked for. (Creating an alias as opposed to breaking aliasing.)Patrizia
@Michael Carman ysth had already given the right answer, I was just pointing out that it is possible to create your own aliases.Webworm
P
3

The only difference between these loops:

foreach (@array) { ... }
foreach my $var (@array) { ... }

is the loop variable. The aliasing is a function of foreach, not the implicit variable $_. Note that this is an alias (another name for the same thing) and not a reference (a pointer to a thing).

In the simple (and common) case, you can break the aliasing by making a copy:

foreach my $var (@array) {
    my $copy = $var;
    # do something that changes $copy
}

This works for ordinary scalar values. For references (or objects) you would need to make a deep copy using Storable or Clone, which could be expensive. Tied variables are problematic as well, perlsyn recommends avoiding the situation entirely.

Patrizia answered 18/6, 2009 at 16:26 Comment(2)
or Clone::More or Clone::FastQuenna
or make a copy in the foreach with for my $var (() = @array)Feature
C
1

I don't know how to force the variable to be by-value instead of by-reference in the foreach statement itself. You can copy the value of $_ though.

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

use Data::Dumper;

my @data = (1, 2, 3, 4);

print "Before:\n", Dumper(\@data), "\n\n\n";

foreach (@data) {
    my $v = $_;
    $v++;
}

print "After:\n", Dumper(\@data), "\n\n\n";

__END__
Cylindrical answered 18/6, 2009 at 15:22 Comment(3)
Where you say my $v = shift; you mean my $v = $_;.Brandybrandyn
@Dave Hinton corrected the error. @Cylindrical try printing $v in the body of your loop as you originally wrote it to see your error.Britney
Yes, I meant my $v = $_; :-(Cylindrical
F
1

Make a copy of @list in the for statement:

foreach my $var1 (() = @list) {
  # modify $var without modifying @list here
}
Feature answered 10/9, 2011 at 4:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.