views:

115

answers:

4

Hey guys, I'm reading the "Beginning Perl" book, and it gives these two statements:

print "Test one: ", 6 > 3 && 3 > 4, "\n";
print "Test two: ", 6 > 3 and 3 > 4, "\n";

The first line prints nothing with a new line, the second line prints a 1 with no new line.

I'm confused about the output. According to the author, the second statement gives weird output because it's just like saying:

print ("Test two: ", 6 > 3) and 3 > 4, "\n";

However, why is the first statement not the same? I thought it had something to do with print's precedence. The && has higher precedence than print, so that's evaluated first and then it's printed. Whereas the "and" has lower precedence than print, so 6 > 3 will be printed, print returns a 1, and then that is evaluated with the "and." However this doesn't really make sense.

I've read the Perl documentation on how precedence works for list operators, but I still do not understand this example. Can you guys dissect the two statements, and tell me what is printed first? Can you also explain what the Perl documentation means when it mentions list operators as being "leftward" and "rightward?" Thanks.

+6  A: 

Here's how those statements would look with full parentheses:

print("Test one: ", ((6 > 3) && (3 > 4)), "\n");
print("Test two: ", (6 > 3)) and ((3 > 4), "\n");

> has the highest precedence, then &&, then ,, then print, then and.

6 > 3 evaluates to 1. 3 > 4 evaluates to false, which in Perl is a special value that is 0 in a numeric context but the empty string in a string context (like here). So ((6 > 3) && (3 > 4)) yields the empty string.

Thus, the first statement passes 3 arguments to print: "Test one: ", the empty string, and a newline. It prints each argument in order.

The second statement passes only 2 arguments to print: "Test two: " and 1. The newline doesn't get printed because it never got passed to print.

I'm not sure how to explain "leftward" and "rightward" better than the docs. But maybe what's confusing you is that you're thinking there are "leftward list ops" and "rightward list ops". That's not what it means. It's trying to say that everything on the right-hand side of a list operator gets interpreted as the arguments for that operator (unless you hit one of the very-low-precedence Boolean operators like and). But things on the left-hand side of a list operator are not associated with that list operator.

cjm
@hobbs: In what way is it not correct? musikk and I said the same thing, except I used some extra parens (and his now-retracted claim that `3 > 4` is `undef`). The output of `perl -MO=Deparse,-p` is identical for the original statements versus my fully-parenthesized versions.
cjm
I'm sorry, it looks like I misread.
hobbs
+4  A: 

It is as you say, except for the "this doesn't make sense" part.

The first is

print "Test one: ", (6 > 3 && 3 > 4), "\n";

and the second

(print "Test two: ", 6 > 3) and (3 > 4, "\n");

If you turn on warnings (use warnings) you get the warning Useless use of a constant in void context because the right side of the and evaluates to a list with elements false and a newline but isn't ever used.

edit: Corrected my 3>4 is undef claim. False is not undef. It's defined and prints nothing.

musiKk
+7  A: 

Okay, for starters: list-operators are among the lowest-precedence things in perl, but only on their right side. You ask what that means: well, let's make it simple. Suppose that there is a listop called foo. It doesn't matter what it does, but it's there. You can easily create such a thing with sub foo { map 10 * $_, @_ } which returns each of its arguments multiplied by ten. That out of the way:

print 1, 2, 3; is equivalent to print( 1, 2, 3 );
print foo 1, 2, 3; is equivalent to print( foo( 1, 2, 3 ) );
print 1, foo 2, 3; is equivalent to print( 1, foo( 2, 3 ) );

We can see that foo gobbles up as much as it can on the right side -- only the end of the statement (so far...) can stop it. If we wrote @array = (1, foo 2, 3); that would be equivalent to @array = (1, foo(2, 3) ); because of course ending surrounding parentheses still applies.

Since commas are also very low priority (just above "List operators (rightward)"), we can also put pretty much any kind of expression we want into the arguments to a listop -- this is just perl's way of making sure that we don't have to add parentheses most of the time. Math, bitwise operators, comparisons, even regex matches have higher priority.

The only things that do have lower-priority are the spelled-out logical connectives and, or, xor, and not. So if we write

foo 1, 2, 3 and 4;

that means (foo(1, 2, 3) and 4) -- the arguments to foo stop to the left of the and. This seems silly in a contrived example, so let's turn it into a common perl idiom:

open $fh, '<', $filename or die "$! opening $filename";

which is equivalent to

(open($fh, '<', $filename) or die("$! opening $filename"));

which is actually exactly equivalent to (and compiles to)

die "$! opening $filename" unless open $fh, '<', $filename;

using the statement-modifier form of unless (which isn't an operator at all, is allowed only once per statement, and only comes at the end of a statement, so it doesn't really engage in precedence at all, but you could consider it as "lower than lowest" precedence leftwards).

So anyway, returning to your original code sample -- the arguments to print and just left of the and, and to the right of the and is a completely separate comma expression -- which does nothing at all because it's just a few constants 3 > 4 and "\n" evaluated in void context.

hobbs
++ : Stellar explanation!
Zaid
A: 

Thanks a lot everyone for your answers! I understand it now. I was indeed doing what cjm said, and thinking that there are leftward and rightward list operators, haha. So now that I understand what it means, I understand the whole thing.

Thanks again guys! (I'm new to this thing, I guess it's frowned upon to make an overall reply back on your own answer?)

Brian
This would probably be better placed in the question (there's an edit link to the left of your signature block). Stack Overflow isn't a forum, and this isn't an answer to your question.
cjm
Also, you should accept the answer that you feel best answered your question. Click on the check mark that will appear just under the score of each answer. (You can only accept one answer.)
cjm