in reply to strict, scope, my and foreach - not behaving as expected

foreach causes $color to be an alias for $colors[0], $colors[1], and $colors[2]. In other words, if you were to assign "pink" to $color on each iteration through the loop, @colors would be filled with 'pink', 'pink', and 'pink'. But the lexical $color, outside of the loop, would remain undefined. This is because of the fact that $color, inside the loop, is a localized alias to the elements of the list you're iterating over. It just happens to be localizing the lexical $color. But when the loop exits, the localized effects are dropped, and $color reverts back to its original self.

The subroutine is defined outside of the loop, and therefore sees the version of $color that exists outside of the loop, and doesn't know about the localized aliases that are taking place inside the loop.

This is a somewhat sloppy version of a closure.

The proper way to pass data into a subroutine is as a parameter. You will find that if you rewrite your snippet with that in mind, your results will be more in keeping with what you predict them to be.

use strict; my @colors = qw ( red green blue ); my $color; my $light = 'on'; foreach $color (@colors) { print "\$color is '$color' and \$light is '$light' in loop, "; print_color( $color, $light ); } #### sub print_color { my ( $clr, $lite ) = @_; print " in sub values are '$clr' and '$lite'\n"; }

Also, if you want to preserve the last value of the loops iterator variable, you should play it safe by copying it to a variable that exists at greater scope, that hasn't been a part of the localized aliasing process.


Dave

Replies are listed 'Best First'.
Re^2: strict, scope, my and foreach - not behaving as expected
by queeg (Novice) on Jun 09, 2004 at 20:54 UTC
    Thanks, Dave.

    To answer some previous comments,

    Why modify an existing program? I want to learn how to 'use strict' (to catch my tpyos) and to scope properly. This is a small, manageable program, and it's easier to understand and correct my mistakes now than after it grows. It's going to be part of a larger project that might even generate <gasp> income. So I want to write it well.

    I'd like to ask 'why' too; why make the iteration variable global? Laziness. There are a few variables (including the outermost loop variable) that all the subs use , and rather than code and keep track of all the passing of parameters to subroutines, I made them global. Of course, I've spent more time trying to understand this issue than I ever would have passing parameters, but hey, that's how you learn.

    What confused me was this: If I satisfy strict with

    foreach my $color (@colors) { ... }
    then I expect $color to be local to the loop. When
    my $color;
    outside of the loop satisfied strict for the loop variable $color I expected $color would be available outside of the loop during iteration. It wasn't.

    There's still one question unanswered - if I use 'our $color' instead of 'my $color', the subroutine can see $color. I know that 'our' scopes globally, and I thought that 'my' at the beginning of the file, outside of any block, would scope to the whole file (effectively global), but it doesn't. So why are they different?

    Thanks,

    Queeg

    use strict; my @colors = qw ( red green blue ); our $color; # our makes it work, my doesn't foreach $color (@colors) { print "\$color is '$color' in loop, "; too_lazy_to_code_passing_parameter(); } #### sub too_lazy_to_code_passing_parameter { print " in sub value is '$color'\n"; } $color is 'red' in loop, in sub value is 'red' $color is 'green' in loop, in sub value is 'green' $color is 'blue' in loop, in sub value is 'blue'