Gerhard Barnard Gerhard Barnard -4 years ago 54
Perl Question

Localizing $_ in for loop is apparently bad, why?

So I have answered a question on SO and got a lot of flack. I have been using perl for many years and used this method quite a lot. So let's start with some code. I am doing search and replace in these examples. The idea is to search for

one
and
three
from two strings and replace them.

$values = 'one two three four five';
$value2 = 'one 2 three four 5';
$values =~ s/one//g;
$values =~ s/three//g;
$values2 =~ s/one//g;
$values2 =~ s/three//g;


So that is very simple code and it everyone will except it. I can also build an array or hash with a list of values to search and replace which is also excepted. However, When I build a script to localize $_ and lessen the amount of typing to build a script, people see it as a massive issue. So here is the code.

$values = 'one two three four five';
$value2 = 'one 2 three four 5';
for ($values, $values2) {s/one//g;s/three//g;}


So my question: The above code will localise $_ in the for block but many programmers are against this, I just need to understand why this is not expectable? Please make me understand why this code is not allowed. If the format is confusing anyone then we can rewrite it in what ever format to make more sense, but it is just the for loop on a scalar that seems to be the issue.

$values = 'one two three four five';
$value2 = 'one 2 three four 5';
for ($values, $values2) {
s/one//g;
s/three//g;
}


it is purely for me to understand why using a
for ($foo)
block is so unacceptable. Thanks a million in advance!

Answer Source

There are several points to consider.


Your code performs multiple substitutions on a list of variables. You can do that without using $_:

for my $s ($values, $values2) {
    $s =~ s/one//g;
    $s =~ s/three//g;
}

Personally I think nothing is wrong with the above code.

The general problem with $_ is that it's not a local variable. E.g. if the body of your for loop calls a function (that calls a function ...) that modifies $_ without localizing it (e.g. by assigning to it or using a bare s/// or using while (<...>)), then it will overwrite the variables you're iterating over. With a my variable you're protected because other functions can't see your local variables.

That said, if the rest of your code doesn't have this bug (scribbling over $_ without localizing it), $_ will work fine here.


However, the code in your answer people originally complained about is different:

for ($brackets) {
    s/\\left/(/g;
    s/\\right/)/g;
}

Here you're not trying to perform the same substitutions on many variables; you're just saving yourself some typing by getting rid of the $brackets =~ part:

$brackets =~ s/\\left/(/g;
$brackets =~ s/\\right/)/g;

Using an explicit loop variable wouldn't be a solution because you'd still have to type out $foo =~ on every line.

This is more a matter of taste. You're only using for for its aliasing effect, not to loop over multiple values. Personally I'm still OK with this.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download