periapt has asked for the wisdom of the Perl Monks concerning the following question:
While considering a previous perl question, I was reminded of an odd occurance for a bit of code. The line $idx{$val} ||= 1 && push @num, $val; in the below snippet will store the selection count of $val into $idx{$val} rather than the value 1 as expected. For example, if the number in $val is the fourth number selected from the random sequence , this code will store the value 4 in $idx{$val}. I'm at a loss as to why this would happen. Interestingly, if the same value occurs in the random sequence a second time (ie the $idx{$val} is already defined), the code will not change the count in $idx{val}. Any ideas?
my %idx = ();
my $val = 0;
my @num = ();
my $nrndded = 3;
my $ulmt = 10;
while(scalar keys %idx < $nrneeded){
$val = int(rand($ulmt-1)+1); # map range to (1..ulmt)
$idx{$val} ||= 1 && push @num, $val;
}
Re: Short Circuit Operator and Hash Assignment
by hardburn (Abbot) on Mar 17, 2004 at 16:29 UTC
|
$idx{$val} ||= push(@num, $val);
The 1 && part is being optimized out of the statement.
Note, too, that the exact operator precedance in Perl5 is poorly defined, and there are many edge cases that don't work out quite as you'd expect.
----
: () { :|:& };:
Note: All code is untested, unless otherwise stated
| [reply] [d/l] [select] |
|
Note, too, that the exact operator precedance in Perl5 is poorly defined, and there are many edge cases that don't work out quite as you'd expect.
Gah, you make it sound as though Perl rolls dice to determine operator precedence. The exact operator precedence in Perl is quite well-defined, and there are no random number generators involved. But people fail to understand it in two different ways. First, they simply don't bother to learn the precedence table, which was the problem in this case (and is the usual problem). I think people get lazy
because operator precedence usually DWIMs, so they figure it
will always DWIM, and then are surprised when it doesn't.
The other way that people fail to understand operator precedence is that they don't know when it doesn't apply. It's important to understand that a yacc grammar uses operator precedence only to break ties between ambiguous rules in the grammar. If the grammar specifies that something can only be parsed in a particular way, operator precedence never comes into it. You may think that the $ on the front
of a variable is a unary operator, but it's not, because what comes after it is not allowed to be a general expression. The grammar restricts the following item to be an identifier, a simple scalar variable, or a block, and these are all recognized by the grammar without recourse to operator precedence. If you try to treat $ as a unary operator, you will certainly be surprised when it doesn't work that way. But it's not an "edge case" of operator precedence. It's simply getting nowhere near the operator precedence table in the first place.
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
|
|
|
The 1 && part is being optimized out of the statement.
this seems a rather dangerous and naive optimization to me, given that also this:
$x = 1 && $y = 2;
$x = 0 || $y = 2;
gets "optimized" as:
$x = $y = 2;
$x = $y = 2;
perhaps the p5p people should do something about it.
cheers,
Aldo
King of Laziness, Wizard of Impatience, Lord of Hubris
| [reply] [d/l] [select] |
|
this seems a rather dangerous and naive optimization to me
The optimization is a red herring; it's not changing
the result in any way. If the left operand of the
&& operator is true in boolean context,
then it returns the value of its right operand,
whatever that may be. Expecting it to return the
left operand is wrong. (What you really were
expecting is a different parse, based on different
operator precedence. (See my other post downthread.))
;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}}
split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print
| [reply] [d/l] |
|
if( 1 && foo() ) {
. . .
}
elsif( 0 || bar() ) {
. . .
}
This optimization is hardly unique to Perl. I believe both C and Java compilers will optimize away constants in exactly the same way.
----
: () { :|:& };:
Note: All code is untested, unless otherwise stated
| [reply] [d/l] [select] |
Re: Short Circuit Operator and Hash Assignment
by dragonchild (Archbishop) on Mar 17, 2004 at 17:56 UTC
|
From the Camel (3rd ed.) pp. 87-88, you'll see that && has a higher precendence than =. So, your statement actually looks like:
$idx{$val} ||= (1 && push(@num, $val));
Then, and only then, does the optimizer deal with the '1 &&' part. This is in contrast to using 'and' instead of '&&', which binds looser than = (and its sibling ||=). Try it out!
------
We are the carpenters and bricklayers of the Information Age.
Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.
| [reply] [d/l] |
Re: Short Circuit Operator and Hash Assignment
by jonadab (Parson) on Mar 17, 2004 at 18:10 UTC
|
Your code is hitting the difference between
&& and and. The former
is for logically anding two simple values together
within a larger expression; the latter, spelled-out,
low-precedence and is for anding together larger
expressions, which is what you're doing here. You
should be spelling out and in this case.
Or, when in doubt, parenthesize.
It may be noted that in practice and
is a much more common and useful operator than
&&, though both have their uses.
They both short-circuit in the same way, and I
suspect that both might also optimize the same
way in a parenthesized case, though what gets
optimized is really an internals issue that does
not make a lot of difference at the language level.
(In your code, if the 1 && were not
optimized away, the result would still be the
same and would still not be what you want. The
optimization is just an implementation detail.)
There are languages that are largely immune to this
sort of issue, due to not having complex precedence
tables. Most lisp-based languages fall into this
category: Common Lisp, Scheme, elisp, and so on.
But to work with those languages, you really
NEED an editor that does paren matching or
automatic indentation -- preferably both.
;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}}
split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print
| [reply] [d/l] [select] |
Re: Short Circuit Operator and Hash Assignment
by Not_a_Number (Prior) on Mar 17, 2004 at 20:06 UTC
|
| [reply] [d/l] [select] |
Re: Short Circuit Operator and Hash Assignment
by periapt (Hermit) on Mar 17, 2004 at 20:07 UTC
|
There you have it. Precedence. Seems a lot less interesting now. I see that the statement is assigning the array length to $idx{$val}. That works actually since the only purpose of the assignment is to define the key in the hash. Kind of an unnecessary evaluation though. Oh well, nothing like getting caught with your parenthesis down.
Thanks for the input.
| [reply] |
|
|