Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

NEWBIE Brain Teaser

by nysus (Parson)
on Apr 14, 2001 at 23:13 UTC ( [id://72605]=perlquestion: print w/replies, xml ) Need Help??

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

OK, newbies! It's time for the first installment of the PerlMonks.org Brain Teaser. Click For some background on these "Brain Teasers". A reminder to all you Perl gurus out there, this is for newbies only! If you can come up with the answer without having to think very hard, please don't bother with this. If you learn something from this exercise, though, feel free to vote this node up.

The code below is designed to give you some practice with conditional statements. What do you predict will be output by this code? You can reveal the answer on the "replies" page. How this code works will be posted soon so be sure to bookmark this node.

#!/usr/bin/perl -w 1 use strict; 2 3 my @list = qw(1 2 3 4 5); 4 my $each = ""; 5 my $i = 0; 6 my @newlist = (); 7 8 foreach $each (@list) { 9 $each *= 2; 10 $newlist[$i] = $each; 11 ++$i; 12 } 13 14 for ($i= 0; $i<5; ++$i) { 15 print ($newlist[$i] - $list[$i], "\n"); 16 }

Replies are listed 'Best First'.
Re: NEWBIE Brain Teaser
by Macphisto (Hermit) on Apr 15, 2001 at 05:50 UTC
    First of all: ++ nysus and kudra for promoting learning
    Since nobody posted the reason why it does this I figured I'd do so. In fact, instead of waste my time, I just took this from "Learning Perl" written by Randal Schwartz, Published by OReilly Associates.

    If the list you are iterating over is made of real variables rather than some function returning a list value, then the variable being used for iteration is in fact an alias for each variable in the list instead of being merely a copy of the values. It means that if you change the scalar variable, you are also changing that particular element in the list that the variable is standing in for. For example:

    @a = (3,5,7,9);
    foreach $one (@a) {
    $one *= 3;
    }
    # @a is now (9,15,21,27)

    Notice how altering $one in fact altered each element of @a. This is a feature, not a bug.


    Macphisto the I.T. Ninja

    Everyone has their demons....

      Thanks for the explanation. I've learned something new about Perl - which isn't too difficult for me!

      I'm just wondering why it's set up this way? Is there an overall principle I can apply to lists, arrays, scalars, hashes, etc., that will help me understand behavior like this?

      Or perhaps I should go back and finish reading "Learning Perl" instead of picking up little bits here and there as I need them. :-\

      Chumley

        Well, and someone please correct me if I mislead on this post,

        If you think about the code:
        foreach $each (@list) {
        $each *= 2;
        $newlist$i = $each;
        ++$i;
        }


        From Programming Perl: Note that the loop variable becomes a reference to the element itself, rather than a copy of the element. Hence, modifying the loop variable will modify the original array.

        The snippet loops through the contents of @list and as each cell gets looked at, it is assigned to $each. $each is then modified by multiplying by 2. Modifying $each, modifies the value in the shell, due to the way foreach is constructed in perl. I believe that is all correct. If I have made any mistakes ( other than grammar, and spelling ), someone please tell me.

        Macphisto the I.T. Ninja

        Everyone has their demons....
Re: NEWBIE Brain Teaser
by nysus (Parson) on Apr 14, 2001 at 23:15 UTC
    Highlight the black box below to reveal the answer.
    Answer

    The code outputs the following five lines:

    0 0 0 0 0
    Is this what you said? Or did you expect...
    1 2 3 4 5
    ...instead?
Re: NEWBIE Brain Teaser
by nysus (Parson) on Apr 15, 2001 at 07:41 UTC
    Now that the cat's out of the bag, I'll share the explanation I had written up...a bit more long winded:

    HOW THE CODE WORKS

    So what's going on here? At first glance, this bit of code seems pretty straightforward. Line 1 assigns numbers 1 thru 5 to the first 5 elements of the @list array. Lines 3-6 do nothing but declare variables. The foreach loop starting on line 8 assigns each element of @list to $each and multiplies the contents of $each by two. It then assigns each $each to an element of the @newlist array. Finally, the for loop starting on line 14 simply steps through each element of both the @newlist and @list arrays simultaneously and subtracts one array from the other. You would expect the first iteration of the for loop to be 2-1 yielding "1", the next iteration to give 4-2 yielding "2", and so on. So what's the problem here?

    The answer lies in the first statement of the foreach block, $each *= 2;. What happens is that the @list array gets aliased through the $each variable. What does this mean? It means that whatever you do to the $each variable in the foreach loop, you also do to the corresponding element of the @list array. So, for example, when $each is equals "3" and it gets multiplied by "2", the third element of @list gets multiplied by "2", too, setting it's value to "6". And there you have the reason why the output of this code is nothing but 5 zeroes!

      Alright, I also expected a return of 1,2,3,4, and 5 as the values printed out. However, I think I have learned where I went wrong. I just wanted to make sure I understood the last paragraph of HOW THE CODE WORKS.

      If I understand this correctly, the $each variable actually acts as a reference to the current place in the array that the foreach loop is currently at. So in effect we are actually altering the first array with the $each *= 2; and then simply setting the $i place in the @newlist to be the same value. This is the reason why we are subtracting, in the last loop, the exact same values from each other.

        Yes, although I do not know if "referencing" is the accurate term. As the post above mine pointed out, the book "Learning Perl" uses the term alias. How that is different from referencing, I'm not entirely certain and maybe a more monkish Monk than I could answer that.
Re: NEWBIE Brain Teaser
by physi (Friar) on Apr 15, 2001 at 01:01 UTC
    This is a very good example for a very confussing, but very OK behavior.
    I'm happy to use ptkdb to figure it out :-)
    Go on nysus, it's a very good idea to create this brain teaser!
    ++ to you
    ----------------------------------- --the good, the bad and the physi-- -----------------------------------
Re: NEWBIE Brain Teaser
by spacewarp (Pilgrim) on Apr 15, 2001 at 07:53 UTC
    I think that the points about printing an explanation for the results are valid, but I would recommend taking it a step further. Since these teasers have a non-obvious result, I think people would also like to see code that does give the answer that was originally expected. In this case, for instance, how to modify the code so that it prints "1 2 3 4 5". After all, I'm sure we've all experienced the frustration of bugs in our logic; for a newbie the frustration would be worse.

    Spacewarp

    DISCLAIMER:
    Use of this advanced computing technology does not imply an endorsement
    of Western industrial civilization.
Re: NEWBIE Brain Teaser
by kha0z (Scribe) on Apr 15, 2001 at 01:09 UTC
    I expected:
    1
    2
    3
    4
    5
    Update:I understand what is going on now. Thanks for the clarification kundra.

    Another Update:I just want to make sure that I understand this. In the "foreach" loop $each is assigned a reference to a scalar value in @list. So when $each is modified in the loop what is actually being modified is $list[$i] which happens to be same value as $newlist[$i]. Right?

    Good Hunting,
    kha0z

      If you want to understand what's going on, you can try adding some print statements. This should make it pretty clear what's happening.
      use strict; my @list = qw(1 2 3 4 5); my $each = ""; my $i = 0; my @newlist = (); foreach $each (@list) { print "Each was: $each\n"; #new $each *= 2; print "Each is: $each\n"; #new $newlist[$i] = $each; print "New list is: $newlist[$i]\n"; #new print "Old list is $list[$i]\n\n"; #new ++$i; } for ($i= 0; $i<5; ++$i) { print ($newlist[$i] - $list[$i], "\n"); }
      And maybe in the future it would be a good idea for the reason for what happens to be included in the 'black box of enlightenment' so that people won't just be puzzled, but will learn, too? (Or maybe not; it's good debugging practice to figure out where the unexpected happens. Why have the box at all? There isn't much point to just show the output, since people can just run the program to see the output.)
        Thanks for the feedback. I was debating whether or not to post the solution or some kind of hint along with the answer. I'll try some different things in the future and see what seems to be the most helpful to people. For this one, I plan on posting the answer tomorrow.

        I think if people can immediately check the answer and see that their first instinct was wrong, this will tend to get them involved more and make them curious...I think they'll start wondering where they went wrong and figure it out for themselves.

        Why the black box? Just for aesthetic reasons...makes it a little more fun, I think.

        kudra said:
        And maybe in the future it would be a good idea for the reason for what happens to be included in the 'black box of enlightenment' so that people won't just be puzzled, but will learn, too?

        I agree, some further explanation to the answer should be added.

        jmoen

A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://72605]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (2)
As of 2024-03-19 06:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found