Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?

Re: NEWBIE Brain Teaser #2, by nysus

by nysus (Priest)
on Apr 16, 2001 at 19:51 UTC ( #72848=note: print w/replies, xml ) Need Help??

in reply to NEWBIE Brain Teaser #2, by nysus


Part A
All you have to know to answer this accurately is how to run a Perl script---just plug and chug the code. You'll see that the program yields "In the beginning there was , in the end there will be ." Kind of profound, no?

Part B
Part B takes a little more work. From the output, it appears that nothing---and I mean nothing, not 'nothing'---gets passed to the subroutine. Now take a look at line 7. Notice the empty set of parentheses? You could translate the empty set of parentheses in plain english to mean "nothing will get passed to the subroutine". So then, the answer to why we get what we get in Part A is that nothing is getting passed to the subroutine!

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!

Part C
The first thing to understand is that this code will behave differently depending on whether you are using Perl version 5.005 or 5.6. We'll go over both here.

Version 5.005 Behavior (the version I used)
In my discussion of "stunt" variables, one thing I neglected to tell you is that it is possible to force the "movie star" variable (the original "@_" array) to perform its own stunt. In other words, you can force the subroutine to use whatever was in "@_" before the subroutine was called. To get a clear understanding of what I mean, do the following:
---Change line 7 per the instructions for Part C
---For now, delete line 6. We'll come back to it later.
---Run the program
You should see that the output of the code is now, "In the beginning there was alpha, in the end there will be omega." The important thing to note here is that by adding the ampersand and removing the parentheses, you force the subroutine to accept whatever the contents of "@_" were before the subroutine was called. Back to our Hollywood analogy, the movie star is doing his/her own stunts. Now, will you ever be coding anything like this in the real world? Probably not. But remember, this is just a's meant only to exercise the Perl muscle in your brain and make it stronger. The better you are at thinking about how Perl behaves, the better Perl programmer you'll be...I think. :-)

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 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!

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://72848]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (8)
As of 2018-01-23 16:20 GMT
Find Nodes?
    Voting Booth?
    How did you see in the new year?

    Results (248 votes). Check out past polls.