|laziness, impatience, and hubris|
Re: NEWBIE Brain Teaser #2, by nysusby nysus (Curate)
|on Apr 16, 2001 at 19:51 UTC||Need Help??|
HOW THE CODE WORKS
Some of you might be wondering why we didn't get "alpha" and "omega" in our output. While a quick perusal of perlsub might lead you to believe this would happen, a closer inspection of the perlsub document explains why it doesn't. I'll just tell you: The reason why "alpha" and "omega" aren't passed to the subroutine is that the "@_" array gets locally scoped. This means that the original contents of "@_" (in this case "alpha and "omega) get temporarily placed into storage while a new "@_" appears on the scene to hold the arguments passed to the subroutine. Once the subroutine finishes, the original contents of "@_" get restored (back to "alpha" and "omega"). A good way to understand what is happening is to think about a Hollywood stunt double. As you probably realize, most movies use a stand-in instead of the "real" actors to do anything even slightly dangerous. In programming jargon, the stunt double gets locally scoped into the movie---he/she replaces the original actor to perform the stunt. Once the stunt is over, the original actor will reappear to act out the rest of scene. Applying this to our bit of code, since no arguments are getting passed to the subroutine, our "stunt variable", "@_", will be empty and this is the complete reason why we get what we get in Part A.
Finally, there may be a question as to what line 6 is doing here. Well, just read what's in the parentheses for the answer: NOTHING and NOTHING. I originally put this line into the code only to add an element of confusion. But to my amazement, and I'm sure even to the amazement of many seasoned Perl programmers, it does come very much into play in Part C. Read on, we're just getting warmed up!
Version 5.005 Behavior (the version I used)
Now all we have left is to do is settle the matter of how the code works with line 6 involved. I myself was baffled when I ran the code with the change to line 7 and saw In the beginning there was nothing, in the end there will be nothing. I fully expected line 6 to be ignored (as it was in the original code) and see "alpha" and "omega" in the output. Instead, I created by own Frankenstein and got stumped by my own brainteaser! I'll do my best to explain what's going on. Remember that this applies only to Perl version 5.005.
To understand this mystery, throw in print "@_"; between lines 6 and 7. Ah ha! We now see that the two elements of "@_" are no longer 'alpha' and 'omega' but are 'nothing' and 'nothing'. From this we can only conclude that line 6, $_ = qw(nothing nothing);, is assigning its list to "@_". But how? Why does assigning something to the "$_" scalar variable in line 6 affect the "@_" array variable? Let's see if we can't isolate the problem further:
That "$_" variable is a little scary to me. Who knows what built-in magical functions it might harbor? Let's get rid of it. Keep the last change you made (assuming you did) of adding print "@_"; to the code. Now change line 6 from $_ = qw(nothing nothing); to my $hello = qw(nothing nothing);. Run the program. Interesting! We see that our line 7, print "@_"; still gives us 'nothing' and 'nothing'. From this we can deduce that something funny is going on with the qw function. Hang in there, we're almost home!
This sounds like a case for perldoc to help us solve. So we consult perldoc and get the following on our prime suspect, qw: qw is equivalent to splitting a single-quoted string on whitespace. Ah ha! So qw behaves a little differently than we thought. The qw funtion is really a split function in disguise!!!
So let's consult the perldoc again and see if we can't find out about our new suspect. And there in the second paragraph of the split function "Description" we find: "If split is used not in list context, split returns the number of fields found and splits into the @_ array....The use of implicit split to @_ is deprecated, however, because it clobbers your subroutine arguments." Well, what do you know? "Clobbering" our subroutine argument sounds exactly like what happened to us!
So, in essence, what happened is that when we used qw in a scalar context, it called the split function which returned the number '2' and assigned it to our scalar variable. (You can see this for yourself if you print out the contents of "$_".) The split function, used in this scalar context, also rudely dumped its contents into our "@_" variable. And so this is why we see 'nothing' and 'nothing' popping out of our subroutine. Got it?
Version 5.6 Behavior Version 5.6 makes things a whole lot easier to explain. It appears that the qw function never calls on the split function to do its work. I confirmed this by checking out http://www.perldoc.com/perl5.6/pod/perldelta.html. Indeed, it says the following: The qw// operator is now evaluated at compile time into a true list instead of being replaced with a run time call to split(). This removes the confusing misbehaviour of qw// in scalar context, which had inherited that behaviour from split(). So now, when you run this script under Perl 5.6, you will get:
"In the beginning there was alpha, in the end there will be omega."
And there you have it!