Why a different result?

J

James

Expect the same output, but differ. Why?

use Data::Dumper;

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

undef %o, %e;
for $f (1..$N) {
$f%2 ? $o{$f}= -1 : $e{$f}=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
 
T

Tim McDaniel

Expect the same output, but differ. Why?

use Data::Dumper;

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

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

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{$f}= -1 : $e{$f}) = 1;

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

You can see this with

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

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

((($f % 2) ? ($o{$f} = (-1)) : $e{$f}) = 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.
 
J

James

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{$f}= -1 : $e{$f}) = 1;

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

You can see this with

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

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

    ((($f % 2) ? ($o{$f} = (-1)) : $e{$f}) = 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.

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

James
 
T

Tim McDaniel

?: binds tighter than =, so this is equivalent to
($f % 2 ? $o{$f} = -1 : $e{$f}) = 1; ....
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{$f} = -1 or $e{$f} = 1;

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{$f} = 0 or $e{$f} = 0;

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

$f % 2 and ($o{$f} = 0, 1) or $e{$f} = 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"
 
U

Uri Guttman

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
 
U

Uri Guttman

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
 
U

Uri Guttman

J> Thanks. So parenthesizing individual expressions is the key.
J> ($f%2) ? ($o{$f}= -1) : ($e{$f}=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
 
T

Tim McDaniel

J> Thanks. So parenthesizing individual expressions is the key.
J> ($f%2) ? ($o{$f}= -1) : ($e{$f}=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.

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.
 
T

Tim McDaniel

J> undef %o, %e;

first off it isn't even needed before the first loop.

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.
secondly it doesn't do what you think it does.

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{'sparkly'}. 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
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.

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{0..1000} = ();
print '%x is ', scalar(%x), "\n";
delete @x{0..1000};
print '%x is ', scalar(%x), "\n";
@x{0..1000} = ();
%x = ();
print '%x is ', scalar(%x), "\n";
@x{0..1000} = ();
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.)
and it leads to a worse problem which is using defined on aggregates
to see if they have any elements.

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

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{'fred'} = 'barney';
$x{23} = 45;
delete $x{23};
delete $x{'fred'};
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.
 
T

Tim McDaniel

(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.)

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.)
 
R

Rainer Weikusat

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

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 ...
 
T

Tim McDaniel

Ben, thank you for your clear and cogent reply (as usual). I quite
agree with you that restricting scope of a variable by
{
my (%o, %e);
...;
}

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?)".
 
T

Tim McDaniel

Whats wrong with using ?: in an expression that is not part of a
larger expression, e.g.,

my $lhs = $foo ? $bar : $baz;

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top