Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl's foreach and changing the looping variable

Tags:

foreach

perl

I am writing a script in Perl and have a question about Perl's foreach construct.

It appears that if you change one of the loop variables it changes in the actual array. Is this in fact the case, or have I done something completely wrong?

I want to change a string like abc.abc#a to abc_abc_a (underscores for non alpha-numeric characters), but I need to preserve the original value in the array for later use.

I have code that looks something like this:

@strings = ('abc.abc#a', 'def.g.h#i');
foreach my $str (@strings){
    $str =~ s/[^0-9A-Za-z]/_/g;
    print $str, "\n"; #Actually I use the string to manipulate files.
}

I could solve the problem by doing the following:

@strings = ('abc.abc#a', 'def.g.h#i');
foreach my $str (@strings){
    my $temp = $str; #copy to a temporary value
    $temp =~ s/[^0-9A-Za-z]/_/g;
    print $temp, "\n"; #$str remains untouched...
}

but is there a more efficient way to accomplish this?

Thank you very much!

like image 680
KLee1 Avatar asked Jul 09 '10 23:07

KLee1


People also ask

What is VAR in foreach loop?

The basic form of the foreach statement is. foreach var (list) block of statements. The variable var will take on each value stored in list in turn, executing the block of statements and continuing on to the next value in the list.

How do I end a foreach loop in Perl?

In many programming languages you use the break operator to break out of a loop like this, but in Perl you use the last operator to break out of a loop, like this: last; While using the Perl last operator instead of the usual break operator seems a little unusual, it can make for some readable code, as we'll see next.

What is foreach loop 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.


1 Answers

You're not crazy; this is normal behaviour. See perldoc perlsyn under 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.

Other loop iterators such as map have similar behaviour:

map BLOCK LIST  
map EXPR,LIST

...
Note that $_ is an alias to the list value, so it can be used to modify the elements of the LIST. While this is useful and supported, it can cause bizarre results if the elements of LIST are not variables. Using a regular "foreach" loop for this purpose would be clearer in most cases. See also "grep" for an array composed of those items of the original list for which the BLOCK or EXPR evaluates to true.

You could rewrite your code this way, which would at least save you from adding an extra line:

my @strings = ('abc.abc#a', 'def.g.h#i');
foreach my $str (@strings){
    (my $copy = $str) =~ s/[^0-9A-Za-z]/_/g;
    print $copy, "\n";
}
like image 108
Ether Avatar answered Oct 17 '22 09:10

Ether