Herkum has asked for the wisdom of the Perl Monks concerning the following question:

I was doing a relatively simple piece of code,
for my $var (@vars) { print "Value: " . $hash_ref->{lc $var} . "\n" if exists $hash_ref- +>{lc $var}; }
I had to lc() the $var variable to for the key comparision. I started thinking, is there another way to do this? Well I could have done this,
for my $var (@vars) { my $new_var = lc $var; print "Value: " . $hash_ref->{$new_var} . "\n" if exists $hash_ref +->{$new_var}; }
Then I started thinking, what if I wanted to do this except it was invalid Perl.
for my $var = lc $_ (@vars) { print "Value: " . $hash_ref->{$var} . "\n" if exists $hash_ref->{$ +var}; }
NOW, I am thinking, how does a value get from @vars to my $var? If there is an internal iterator used by the for loop, does it put a value into the variable assignment or can you put an expression in there in some way? I admit I have never seen it before, but it is Perl!

Replies are listed 'Best First'.
Re: How a for() assignment works
by jwkrahn (Abbot) on Mar 06, 2008 at 21:24 UTC

    Perhaps you want:

    for my $var ( map lc, @vars ) { print "Value: " . $hash_ref->{$var} . "\n" if exists $hash_ref->{$ +var}; }
      Had not thought of doing something inside the (), though I guess I should have known better.
Re: How a for() assignment works
by NetWallah (Canon) on Mar 06, 2008 at 21:31 UTC
    This could be done thus:
    for my $var (map {lc} @vars) { print "Value: " . $hash_ref->{$var} . "\n" if exists $hash_ref->{$ +var}; }

    Update: Looks like jwkrahn beat me to it...

         "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom

Re: How a for() assignment works
by amarquis (Curate) on Mar 06, 2008 at 21:32 UTC

    Wow, you caused me to have quite the headslap moment. At first I was thinking, why does the lc get assigned to a new variable? So I played around with it and realized for the first time that $var in this construct is an alias and not a copy. *headslap*

    Could you not, at least in this example, use a map? Like, map { print ("Value: " . $hash{lc($_)}); } @valuesIs it uncouth to use map this way?

    Edit: Destroyed by better and faster answers!

      Yes - I consider it uncouth.

      "map" (and "grep") return lists. If you are not using the returned values, and merely looping, you should use "for".

           "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom

        Your use of "uncouth" seems a bit harsh, especially given that FYI, starting with Perl 5.8.1 "map" no longer returns anything when called in void context. See Perl 5.8.1 Miscellaneous-Enhancements for more.

        Update: Sorry about that NetWallah. I missed it in the OP due to the poor formatting. Thanks for pointing it out! I will strike out that portion of my message (and bold the updates).

Re: How a for() assignment works
by CountZero (Bishop) on Mar 07, 2008 at 07:07 UTC
    for my $var (map lc, grep exists $hash_ref->{lc $_}, @vars) { print "Value: " . $hash_ref->{$var} . "\n" ; }
    or even
    for my $var (map $hash_ref->{lc $_}, grep exists $hash_ref->{lc $_}, @ +vars) { print "Value: " . $var . "\n" ; }
    finally leading to
    print "Value: " . $_ . "\n" for map $hash_ref->{lc $_}, grep exists $hash_ref->{lc $_}, @vars;
    but if we swap the map and grep we can eliminate the duplicate lc
    print "Value: " . $hash_ref->{$_} . "\n" for grep exists $hash_ref->{$_}, map lc, @vars;
    Of course now we have three loops running :-(

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      Or you could always do it like this:

      print map exists $hash_ref->{lc()} ? "Value: $hash_ref->{lc()}\n" : '' +, @vars;
Re: How a for() assignment works
by igelkott (Priest) on Mar 06, 2008 at 21:38 UTC
    print map {"Value: $hash_ref->{lc $_}\n"} @vars;

    Update:Over zealously forgot to consider "if exists". Better solutions offered.

      You did get rid of the duplication.

      But it no longer prints what the OP wants it to print.
      And it issues warnings.
      And memory usage was needless increased.

      One step forwards, three steps back.

        Guess I got a little carried away and overlooked the important "if exists" requirement. This is the cause of the warnings. Memory increase doesn't seem to be that big a deal but I didn't check this on anything big.

        Thanks for the reminder to look both ways before jumping into traffic.

Re: How a for() assignment works
by repellent (Priest) on Mar 07, 2008 at 17:05 UTC
    Here is how I think about for() assignments:

    For a code like,
    for ($passed_in) { # inside BLOCK }
    Inside the BLOCK, $_ is equal to $$ref where $ref = \$passed_in
    Except that if you did change $_, you will also modify $passed_in. They are magically tied together, $_ and the reference, that is.

    It is faster this way, since only a REFerence is passed_in. No copy of $passed_in is made. However, for the following code,
    for my $scoped_copy ($passed_in) { # inside BLOCK }
    A copy of $passed_in is made to $scoped_copy.

    The same reasoning above applies to other loop constructs, sub arguments, or CODE BLOCKS of map(), grep(), sort(), etc. for the $_ @_ $a $b variables.

    Again, this is just my observation. Would someone with knowledge of Perl guts confirm that I could continue with this train of thought?

      As Foreach Loops explains, the variable, whether it is implicitly $_ or explicitly my $scoped_copy, is an alias to the current element of the list.