Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
why does that print 1..11? If I want I want to print what's on the right, if what's on the right doesn't evaluate as true, is the other option ternary operators? {print for (1..10 ? 1..10 : 1..11);) If you can use an 'or' instead of ternary operators, is it faster?print for (1..10 or 1..11);
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: for loops and 'and'
by Zaxo (Archbishop) on Nov 30, 2003 at 06:10 UTC | |
Ooh, that's weird. The problem is clearly with precedence and context. I have no idea why that doesn't mean print for 1..10;. I can make a case for that meaning print for 10;, but it is crazy that 1..10 evaluates false on the left of low precedence or. I'm eagerly anticipating an explanation. Update: This prints nothing, This must be hooked up with the odd special case mentioned by davido that $. is hooked up with flipflops when thay have constant arguments. Dwimmerie gone berserk. After Compline, | [reply] [d/l] [select] |
by ysth (Canon) on Nov 30, 2003 at 08:18 UTC | |
This must be hooked up with the odd special case mentioned by davido that $. is hooked up with flipflops when thay have constant arguments. Dwimmerie gone berserk.Not gone berserk, just an awkism. Convenient for processing on ranges of lines, e.g. (1..2)&&/foo/||(3..eof)&&/bar/. What sometimes surprises is that it applies to any constant, even nonnumeric. For instance: Here it happily compares int("a") to int("b"), giving warnings on both, and then (since both are 0, hence equal) dies. (Though older perls had a bug where it assumed not-equal if no reading of input had ever been done and $. was manually set.) | [reply] [d/l] [select] |
by Anonymous Monk on Nov 30, 2003 at 07:19 UTC | |
Perl's range operator has wierd behavior, methinks... but why? Is there anything to fix that in my first case? I'm assuming it'd be a pretty large optimization over ternary operators... The problem also existed with arrays that represented ranges, which was even wierder... do_something $_ for ( @_[1..$#_] || 0..$#{$_[0]}) is the code that I was initially working with to no avail... I guess one could say the problem also is that in the case of for loops, @_[1..$#_] is being used in two contexts at the same time, and perl can't deal with that... The code was basically saying- if it it's there (in scalar context), use it in list context, and if it isn't there, use a diff. list. Edit: BazB, added extra code tag. | [reply] [d/l] [select] |
by Roger (Parson) on Dec 01, 2003 at 00:11 UTC | |
And when I ran it, I got the following warnings and results - The warnings and results might give a clue to what is happenning with list/range and the for and or operators? | [reply] [d/l] [select] |
by jweed (Chaplain) on Nov 30, 2003 at 06:51 UTC | |
Read more... (2 kB)
Who is Kayser Söze? | [reply] [d/l] [select] |
|
Re: for loops and 'and'
by ysth (Canon) on Nov 30, 2003 at 08:36 UTC | |
Array slices are different. They don't have a scalar context per se; if scalar context is imposed (i.e. by being the first operand to ?:) the last element of the slice will be returned (and checked for truth by ?:). So assuming your desired-but-not-working-as-is-code was print for @a[@indices] or 1..11 you need to decide what you mean to do by checking @a[@indices] for truth. That could be any of a number of things. Perhaps one of these would be what you meant: I.e. use the array if (any/all) of the elements are (true/defined/existing), otherwise fall back on 1..11. | [reply] [d/l] [select] |
|
Re: for loops and 'and'
by jweed (Chaplain) on Nov 30, 2003 at 05:50 UTC | |
Here's what's happening (I think). The or operator provides scalar context to the first thing it tries (1..10). As the perlop manpage points out, this is false the first time it is tried (I don't really get this...). So for takes 1..11 as its range instead of 1..10. As for your "real life" issue with the array slice and the string, my tests show that or never gives scalar context to the second operator, and that it always does to the first. Why? I don't know. UPDATEOkay, it seems I didn't understand .. before. From perlop: If either operand of scalar ".." is a constant expression, that operand is implicitly compared to the $. variable, the current line number.So what happens depends on where it happens. Screwey, eh? But don't quote me on that Who is Kayser Söze? | [reply] [d/l] |
by pg (Canon) on Nov 30, 2003 at 16:47 UTC | |
No, this demo defeats what you said: (BTW, just for people who does not know, $. is not the line number of your script, but the line input number of last accessed handler. In the demo, the running line number of __DATA__)
This code prints 1..11 twice. | [reply] [d/l] |
by ysth (Canon) on Nov 30, 2003 at 20:27 UTC | |
No, this demo defeats what you saidI'm not sure which part you are responding to. I assume you understand this code, but for others I will point out that the <DATA> for (1..5) leaves $. set to 5 and then <DATA> for (1..7) makes it 12. For neither 1..10 is it equal to 1, so the flipflop returns false. | [reply] [d/l] [select] |
by pg (Canon) on Nov 30, 2003 at 21:12 UTC | |
by ysth (Canon) on Nov 30, 2003 at 22:39 UTC | |
|
Re: for loops and 'and'
by ysth (Canon) on Nov 30, 2003 at 08:04 UTC | |
Try this: perl -wne'print for (1..10 or 1..11)' foo where foo is a file with 12 or so lines. You will get "12345678910E0" as the output for the first 10 lines and "1234567891011" as the output for each line thereafter. To the monk confused by the difference between scalar(1..10) and scalar 1..10, that's a precedence thing. The latter is equivalent to scalar(1)..10 so the scalar() is not forcing .. to have scalar context. | [reply] [d/l] [select] |
|
Re: for loops and 'and'
by Anonymous Monk on Nov 30, 2003 at 06:28 UTC | |
That's what's happening. | [reply] [d/l] |
|
Re: for loops and 'and'
by dpmott (Scribe) on Nov 30, 2003 at 23:49 UTC | |
Just wanted to add my $0.02 on this: 1. perldoc perlop, search for and read the following sections: "C-Style Logical Or" and "Range Operators" (which are conveniently co-located...) 2. Quote from "C-Style Logical Or": So, the suggestion of using the ternary operator is a good one. For large arrays, I usually use references, though, if I know that I won't be modifying the array: (I use the 'int' operator there for readability. It's superfluous, and does the same thing as the 'scalar' operator for that particular example. Also note that the only difference between the '||' operator and the 'or' operator is precedence. 3. I don't think anyone specifically called it out, but the '..' operator returns a boolean *whenever* it is evaluated in scalar context, not just whenever there is a constant in there. Having the constant in there *also* compares that constant to the '$.' variable. I just wanted to chip in that clarification. Sorry if someone did point that out and I just missed it. | [reply] [d/l] [select] |
|
Re: for loops and 'and'
by jonadab (Parson) on Dec 01, 2003 at 15:36 UTC | |
for loops have absolutely nothing to do with your problem. Your problem is context. *Lots* of things in Perl behave differently in scalar versus list context. Arrays and array slices report their cardinality in scalar context (except in interpolative context, which is special); in list context they dump their whole contents. List construction operators (the comma list operator and the .. range operator) only *exist* in list context, because Perl doesn't see any point in constructing a list in scalar context. "Why should I construct a list here?", it says, "The very next thing I'd have to do is change it to a scalar somehow. It's utterly pointless to construct a list." So in scalar context these operators do not construct a list; they do Something Else Different. as far as for (1..10 or 1..11) versus for (1..10 ? 1..10 : 1..11), the leftmost 1..10 evaluates the same way in both cases; the difference is that if it's true, the latter then evaluates 1..10 in list context; whereas, the former takes the true value and just uses it, which is probably not what you want. Actually, the whole thing is probably not what you want if you didn't specifically intend to use the .. operator in scalar context. Frankly, I'm not sure why you would want to do that; the scalar .. operator is sufficiently obscure that the Camel Book (as of the 2nd edition; I haven't read the 3rd yet) doesn't even *mention* it. It's not a terribly common operator. Now, I'm guessing that what you're really attempting in your code is along the lines of for (@a or @b) or maybe for (@a ? @a : @b). The former doesn't do what you want, but you *think* the latter does. But that's an accident; as it happens, the latter isn't working the way you think it is either. The first @a is getting evaluated in boolean context (which is scalar), and so it is returning its *cardinality*. It is not dumping the whole list of its contents like you think. It just happens that a non-empty array or slice will always have a true cardinality (because of the nature of the way cardinality works) and so the second @a will get used when you think it will -- but not for the reason you were thinking, and that's why the simpler or construction doesn't do what you want. If you want to be really efficient programming in Perl, if you want to *think* in Perl, you have got to get comfortable thinking in terms of context. Lists and list constructors are not by any means the only things that behave according to context. Many functions (including builtins, object methods in modules from CPAN, and your own functions if you write them that way), certain special variables, and various operators all do different things in different contexts. It's the richness of the context that makes Perl such a flexible and useful language, but if you don't think in terms of context it will confuse you sometimes. When you think in terms of context, you won't make these sorts of mistakes. When I first saw your print for (1..10 or 1..11) I wasn't sure exactly what it would do (since I'm not very familiar with the scalar .. operator), but I for darn sure didn't think it would print "12345678910"; it would have surprised me a *great* deal if it had done that. My immediate thought was, "What on earth is he trying to do?" When I read the text of your post and determined that you thought it would print "12345678910", I knew you didn't understand context, because there's no such thing as a list in scalar context; it doesn't even make sense.
| [reply] [d/l] [select] |
|
Re: for loops and 'and'
by dga (Hermit) on Dec 01, 2003 at 18:29 UTC | |
One thing I didn't see noted but which may aid understanding. Frequently, in normal use of the boolean operators, they are used for something which has a desired side effect. Then the booleans do something really handy. Here is an example of error notification.
Here the open is evaluated in scalar context for a true/false value but in order to know if its true or false, perl has to actually try to open the file to see, so the file opening is a side effect of the boolean test. From the programmer point of view the file test is what's going on and the 'or' is for error checking, but from the program flow it is trying to find out if the left side of the or is true or false.
This would put the array in scalar context and test for true/false, so if the array has elements then this is true even in the following case.
The 'or' never does the print because the assignment made $array[0]=undef;. Thus the array returns true in scalar context and the print isn't done. If you capture the value of this or then you get back the number of elements in the array. Likewise if you capture the value of the open you get back a true value or undef depending on whether the file was opened or not. Using 'or' in these places, leads one to feel that actual work can be performed by the boolean operators, but in reality, if work is done, as in the open case, it is the desired side effect of the call and not the boolean comparison that the programmer is interested in occurring. In fact, as a programmer, one might be happier that the right side of the 'or' with the open was never evaluated at all since that means that the program is not encountering error conditions. | [reply] [d/l] [select] |