# Why a different result?

#### Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

•  Subject
• Author
• Posted on
Expect the same output, but differ. Why?

use Data::Dumper;

undef %o, %e;
for \$f (1..\$N) {
if (\$f%2) { \$o= -1 } else { \$e=1 }
}
print Dumper \%o; print Dumper \%e;

undef %o, %e;
for \$f (1..\$N) {
\$f%2 ? \$o= -1 : \$e=1;
}
print Dumper \%o; print Dumper \%e;

(output)

\$VAR1 = {
'1' => -1,
'3' => -1,
'7' => -1,
'9' => -1,
'5' => -1
};
\$VAR1 = {
'8' => 1,
'6' => 1,
'4' => 1,
'10' => 1,
'2' => 1
};
\$VAR1 = {
'1' => 1,
'3' => 1,
'7' => 1,
'9' => 1,
'5' => 1
};
\$VAR1 = {
'8' => 1,
'6' => 1,
'4' => 1,
'10' => 1,
'2' => 1
};

James

## Re: Why a different result?

It's helpful to post real programs that run, because then people can
immediately copy and paste it, run "-MO=Deparse,-p", or whatnot.

See the precedence table near the top of "man perlop".
Assignment operators bind more loosely than ?:, so the second method
is equivalent to

(\$f%2 ? \$o= -1 : \$e) = 1;

Assignments return lvalues that you can assign to again, so on odd
numbers \$o gets assigned -1 and then it gets stomped with 1.

You can see this with

\$ perl -MO=Deparse,-p -e '\$f%2 ? \$o= -1 : \$e=1;'
(((\$f % 2) ? (\$o = (-1)) : \$e) = 1);
-e syntax OK

I'll add numbers (in a fixed-width font) to show the paren pairing:

(((\$f % 2) ? (\$o = (-1)) : \$e) = 1);
123      3   3         4  43         2    1

Personally, I don't use ?: much, partially because it can sometimes
lead to too-complicated expressions, and partially because it can
cause precedence problems like this.  Parenthesizing liberally in ?:
can be a good idea.

--
Tim McDaniel, tmcd@panix.com

## Re: Why a different result?

On Mar 27, 1:18=A0pm, t...@panix.com (Tim McDaniel) wrote:

Thanks. So parenthesizing individual expressions is the key.
(\$f%2) ? (\$o=3D -1) : (\$e=3D1) ;

James

## Re: Why a different result?

J> Thanks. So parenthesizing individual expressions is the key.
J> (\$f%2) ? (\$o= -1) : (\$e=1) ;

NO! using ?: the correct way is the key. it is for returning one
expression from the pair. it is NOT for side effects like assignment or
calling functions. if you need that, use if/else. the first version of
the code you had is correct. the second is incorrect and should not be
fixed with parens. that compounds the problem.

uri

## Re: Why a different result?

It was made to work, though with some difficulty, so it is a large
terminological inexactitude to call it flatly "incorrect" and to yell
"NO!"

But it is pointless at best.  The only plausible use of ?: really is
for evaluating the results of expressions *for use in a larger
expression*.  If the results of the right-hand sides are not needed,
as in this particular example, ?: gains nothing over a plain
if-then-else.  For example, it's like calling two subs with

2*a() - b()/17;

What's the point of the useless arithmetic?  That example would be
clearer as just

a(); b();

Further, the original poster hit a pitfall, a point of fragility, due
to precedence -- and much worse, it was a silent bug.

Moreover, others and I generally think it's worse style the more
complicated and side-effecty a ?" expression gets.

So while it cannot be called "incorrect", I think most people would
strongly recommend against the above example.

--
Tim McDaniel, tmcd@panix.com

## Re: Why a different result?

tmcd@panix.com (Tim McDaniel) writes:

Below is a quote from the perlop(1) manpage:

The operator may be assigned to if both the 2nd and 3rd
arguments are legal lvalues (meaning that you can assign to
them):

(\$a_or_b ? \$a : \$b) = \$c;

Because this operator produces an assignable result, using
assignments without parentheses will get you in trouble.  For
example, this:

\$a % 2 ? \$a += 10 : \$a += 2

Really means this:

((\$a % 2) ? (\$a += 10) : \$a) += 2

Rather than this:

(\$a % 2) ? (\$a += 10) : (\$a += 2)

That should probably be written more simply as:

\$a += (\$a % 2) ? 10 : 2;

I would usually avoid the second form because it discards the result
of the ?: and needlessly repeats the variable and the operator in both
terms. OTOH, results are frequently discarded, so that's not a 'hard'
reason. I have certainly used the first form whenever it was
convenient[*]. That ?: returns an lvalue in Perl is different from the
C ?: which implies some conscious design descision.

[*] I actually used to do

(a ? func_a : func_b)(a, b, c);

in C or

(\$a ? \&func_a : \&func_b)->(\$a, \$b, \$c)

in Perl until I sadly realized that no compiler will ever 'optimize
that' in the seemingly obvious way: After all, it's not a common
coding blunder mathematically oriented perpetual newbies with a
desired to avoid learning are wont to make ...

## Re: Why a different result?

Since "=" is an operator (and += -= and all the rest), I count that as
being part of a larger expression.

In any event, I meant "the only really plausible use of ?: is where
the value is used, which means outside of void context" regardless of
whether there's an operator that expressly shows it explicitly.
For example,

foreach (\$use_primary ? @primary : @secondary)

looks OK to me.

--
Tim McDaniel, tmcd@panix.com

## Re: Why a different result?

Quoth tmcd@panix.com:

As I think I've said before, I disagree here. A tree of ?: *on the RHS
of an assignment* is a simple and clear way to represent a list of
(potentially quite complicated) cases.

If you find yourself needing to parenthesise, you're using it for flow
control rather than as an expression, and you should switch to if/else
or given/when instead. This is basically the same rule-of-thumb that
tells you when to use and/or rather than &&/||.

Ben

## Re: Why a different result?

TM> Personally, I don't use ?: much, partially because it can sometimes
TM> lead to too-complicated expressions, and partially because it can
TM> cause precedence problems like this.  Parenthesizing liberally in ?:
TM> can be a good idea.

then you are thinking incorrectly about ?:. it is an expression and not
meant for side effects. = is an assignment and a side effect. if you
just use ?: to select one of two expressions it generally works well
with no precedence issues. and it is usually cleaner than the longer
if/else with redundant assignments. in fact if the lvalue is more then a
variable then the ?: will be much cleaner than an if/else and less prone
to bugs. when you have redundant code, you have to make sure both parts
are the same, you need to change both if one is changed, and the reader
of the code has to check both to see they are the same. ?: eliminates
the redundancy and all that wasteful work.

uri

## Re: Why a different result?

?: binds tighter than =, so this is equivalent to

(\$f % 2 ? \$o = -1 : \$e) = 1;

When \$f is even, this reduces to

\$e = 1;

as you want, but when \$f is odd it reduces to

(\$o = -1) = 1;

which is not what you meant. If you insist on using ?: for flow control,
you need to parenthesise both parts, like this

\$f % 2 ? (\$o = -1) : (\$e = 1);

(Strictly speaking you don't need to parenthesise the first part unless
it contains more ?:s, but it would be a bit weird not to.)

You can also use and...or, but only if you are certain the 'if' part
will always return a true value:

\$f % 2 and \$o = -1 or \$e = 1;

Ben

## Re: Why a different result?

...

It took me a minute to figure out that you meant that the *then* part
must always return true.  (If the "if" part always returned true,
there'd be no need for a conditional!) You make a good point.  Indeed,

\$f % 2 and \$o = 0 or \$e = 0;

would set both %o and %e for odd numbers.

\$f % 2 and (\$o = 0, 1) or \$e = 0;

looks odd and fragile, and is no improvement over a parenthesized ?: .

The idiom works much more reliably in Bourne-like shells, because
commands' boolean value is based on their exit code, not usually the
arithmentic result of an operation:

(( f % 2 )) && echo "\$f is odd" || echo "\$f is even"

--
Tim McDaniel, tmcd@panix.com

## Re: Why a different result?

J> Expect the same output, but differ. Why?
J> use Data::Dumper;

J> undef %o, %e;

others have answered the ?: issue. i want to address that line of
code. it is wrong in several ways.

first off it isn't even needed before the first loop. secondly it
doesn't do what you think it does. the proper way to clear hashes is to
assign an empty list to them:

%o = () ;

undef is not meant to be used on aggregates (arrays and hashes). it not
only clears the data, it reclaims all storage inside it. and it leads to
a worse problem which is using defined on aggregates to see if they have
any elements. and that is very wrong as defined on a hash which has been
undef'ed will be false but if is ever had elements but was empty now,
defined on it will be true. and that is almost never what you expect. so
the rule is don't use undef on aggregates and never use defined on them.

uri

## Re: Why a different result?

Sometimes I like to have an explicit initialization, as documention
that I have considered the issue and believe that the assignment is
needed at that point.  It's also nice if I have to move the block of
code someone else.  But I'm idiosyncratic in that respect -- I suspect
few people do that.

If nothing else, for one reason not mentioned yet.  The undef docs say

undef EXPR
undef

... Note that this is a unary operator, not a list operator.

That is, it takes either zero or one argument.  It certainly doesn't
take two or more.  So the quoted line is equivalent to

(undef(%o)), %e;

It undefs only %o and doesn't touch %e.  %e is evaluated in a void
context, and if it returns a value, it's thrown away.

The output under Perl 5.14 of

#! /usr/bin/perl -w
use strict;
use warnings;
my %x;
my %y = ('sparkly' => "You might think this is deleted by undef");
undef %x, %y;
print "\$y. If so, you'd be wrong.\n";
exit 0;

is

\$ perl local/test/095.pl
Useless use of private hash in void context at local/test/095.pl line 6.
You might think this is deleted by undef. If so, you'd be wrong.

Note well: if the original poster had used
use strict;
use warnings;
as ought to be done, the bug would have been found.

Similarly for "my" taking only one argument (though it can be a list
of variables), and that "use strict; use warnings;" is great.
\$ perl -e 'use warnings; my \$x = 3, \$y = 4;'
Name "main::y" used only once: possible typo at -e line 1.
That is, the warning about \$main::y indicates that \$y was NOT my-ed.
\$ perl -e 'use strict; use warnings; my \$x = 3, \$y = 4;'
Global symbol "\$y" requires explicit package name at -e line 1.
Execution of -e aborted due to compilation errors.
is better, having a fatal error due to the undeclared \$y.
The usual way for multiple initializations in my is with separate
statements
\$ perl -e 'use strict; use warnings; my \$x = 3; my \$y = 4; print "\$x \$y\n"'
3 4
or with lists
\$ perl -e 'use strict; use warnings; my (\$x, \$y) = (3, 4); print "\$x \$y\n"'
3 4

Um, if (by Grice's heuristics) you're trying to imply that %o=() does
NOT reclaim all storage, you just made undef sound better.  In Perl
5.14, the scalar value of the hash is the same for deleting every
existing element, assigning (), or undeffing:

#! /usr/bin/perl -w
my %x;
@x = ();
print '%x is ', scalar(%x), "\n";
delete @x;
print '%x is ', scalar(%x), "\n";
@x = ();
%x = ();
print '%x is ', scalar(%x), "\n";
@x = ();
undef %x;
print '%x is ', scalar(%x), "\n";
exit 0;

results in

\$ perl local/test/097.pl
%x is 630/1024
%x is 0
%x is 0
%x is 0

There may well be a way to look at internals to know how much storage
space is used for each behind the scenes, but I don't know what such a
method might be.

(As a tangent: I think there was a way in Perl 4 to assign to a
subscript of an array, shrink the array to be smaller than the
subscript, regrow past that subscript, and see the assigned value
again.  Does anyone remember the details?  Please tell me that it no
longer works.)

That is to say, using "undef %x" might lead you to think that
"defined %x" is also usable, but "defined" on a hash table is a trap
and a snare, because

Indeed.  To illustrate that,

#! /usr/bin/perl -w
use strict;
use warnings;

my %x;
sub checkx(\$) {
print "After \$_[0]\n";
print "    defined? ", (defined %x ? "yes\n" : "no\n");
print "    boolean? ", (%x ? "true\n" : "false\n");
print "\n";
}
checkx("start");
%x = ();
checkx("empty list");
# Quotes aren't needed around 'fred' or 'sparkly',
# but I'd rather not go into the bareword rules.
\$x = 'barney';
\$x = 45;
delete \$x;
delete \$x;
checkx("deleting last element");
%x = ();
checkx("again empty list");
undef %x;
checkx("undeffing");

exit 0;

results in

\$ perl local/test/094.pl
defined(%hash) is deprecated at local/test/094.pl line 8.
(Maybe you should just omit the defined()?)
After start
defined? no
boolean? false

After empty list
defined? no
boolean? false

After deleting last element
defined? yes
boolean? false

After again empty list
defined? yes
boolean? false

After undeffing
defined? no
boolean? false

That is, "use warnings;" again comes through.  And the program shows
that a simple use of %x in boolean context like
if (! %x)
tells you accurately in each case that %x has no elements, but
if (! defined %x)
is not a reliable indication of that.

--
Tim McDaniel, tmcd@panix.com

## Getting old values back

Found it in "man perldata":

The length of an array is a scalar value.  You may find the length
of array @days by evaluating \$#days, as in csh.  However, this
isn't the length of the array; it's the subscript of the last
element, which is a different value since there is ordinarily a
0th element.  Assigning to \$#days actually changes the length of
the array.  Shortening an array this way destroys intervening
values.  Lengthening an array that was previously shortened does
not recover values that were in those elements.  (It used to do so
in Perl 4, but we had to break this to make sure destructors were
called when expected.)

--
Tim McDaniel, tmcd@panix.com

## Re: Why a different result?

Quoth tmcd@panix.com:

Not at all: it's a common mistake made by those who haven't learned how
to use 'my' properly. The correct way to have written the OP's example
would be

{
my (%o, %e);
...;
}
{
my (%o, %e);
...;
}

That way each block is self-contained, doesn't affect the code outside
it, and can be moved anywhere else in the program safely.

~% perl -MDevel::Peek -e'my %h = 1..20; %h = (); Dump \%h'
SV = IV(0x624288) at 0x624290
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x624080
SV = PVHV(0x60b070) at 0x624080
REFCNT = 2
ARRAY = 0x609230
KEYS = 0
FILL = 0
MAX = 15
RITER = -1
EITER = 0x0

Notice that ARRAY is still set, and MAX is 15, so this hash still has
space allocated for 15 keys (none of which are currently being used,
since KEYS is 0).

~% perl -MDevel::Peek -e'my %h = 1..20; undef %h; Dump \%h'
SV = IV(0x624288) at 0x624290
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x624080
SV = PVHV(0x60b070) at 0x624080
REFCNT = 2
ARRAY = 0x0
KEYS = 0
FILL = 0
MAX = 7
RITER = -1
EITER = 0x0

This time ARRAY has been freed and MAX reset to 7 (which is what it
always starts at).

However, this is not necessarily a good thing: if you are about to reuse
it (and if you aren't, why is it still in scope?) then perl will just
have to reallocate all that memory again, which is a waste of time.
Leaving it allocated is part of perl's general policy of trading space
for speed. It is worth being aware, though, that if you've allocated a
really big hash, *and* you're definitely not going to use that hash
again, that it may pay to explicitly undef it when you've finished using
it.

Ben

## Re: Why a different result?

Ben, thank you for your clear and cogent reply (as usual).  I quite
agree with you that restricting scope of a variable by

is often a good technique, and that @a=() may be more time-efficient
than undef @a if you want to add elements again to @a "(and if you
aren't, why is it still in scope?)".

--
Tim McDaniel, tmcd@panix.com