Re: undef as a key in a hash.
by Joost (Canon) on Sep 25, 2007 at 18:34 UTC
|
perl -we'$b=undef;$a{$b}=1;print$a{""}'
Use of uninitialized value in hash element at -e line 1.
1
update: strictly speaking a hash key (for a normal hash) can't be undef, a reference, an object or anything but a plain string, and using anything as a hash key that isn't a string will use the stringified form. Which explains the behaviour in TedYoung's example.
| [reply] [d/l] |
|
|
strictly speaking a hash key (for a normal hash) can't be undef, a reference, an object or anything but a plain string, and using anything as a hash key that isn't a string will use the stringified form.
That's only true for plain hashes. Tied hashes are not subject to those restrictions, for example. (See code below.) Tied hashes can accept all of those as a key (including undef, although it generates a warning), but tied hashes can't have each/keys/values return undef because it's a sentinel value indicating all of the keys have been visited.
So the question is whether there's another type of magical hash that can have keys return undef.
use strict;
use warnings;
{
package TestHash;
sub keyinfo {
my ($k) = @_;
if (defined($k)) {
if (ref($k)) {
print("reference\n");
} else {
print("string\n");
}
} else {
print("undefined\n");
}
}
sub TIEHASH { my ($c ) = @_; return bless([], $c); }
sub FETCH { my ($s,$k ) = @_; keyinfo($k); return undef; }
sub STORE { my ($s,$k,$v) = @_; keyinfo($k); push @$s, $k; }
sub FIRSTKEY { my ($s ) = @_; return shift @$s; }
sub NEXTKEY { my ($s ) = @_; return shift @$s; }
}
tie my %h, 'TestHash';
foreach my $k ('a', [], undef) { $h{$k} = 1; }
print("\n");
foreach my $k ('a', [], undef) { my $dummy = $h{$k}; }
print("\n");
foreach my $k (keys %h) { TestHash::keyinfo($k); }
string
reference
Use of uninitialized value in hash element at line 29.
undefined
string
reference
Use of uninitialized value in hash element at line 31.
undefined
string
reference
| [reply] [d/l] [select] |
Re: undef as a key in a hash.
by liverpole (Monsignor) on Sep 25, 2007 at 18:47 UTC
|
For fun, here's an example of using undef as a hash key. It validates what Joost and Eimi Metamorphoumai have said, that as a key, an undef is equivalent to the empty string "":
#!/usr/bin/perl -w
use strict;
use warnings;
use Data::Dumper;
my $x; # $x is undef
my $y; # $y is undef too
my %hash;
$hash{$x} = 123;
# This will throw warnings, but both $hash{$x} and $hash{$y} are 123
printf "\$hash{\$x} = %s; \$hash{\$y} = %s\n", $hash{$x}, $hash{$y};
print "Keys of \$hash => %s\n", Dumper([keys %hash]);
__END__
Output:
Use of uninitialized value in hash element at x line 11.
Use of uninitialized value in hash element at x line 11.
Use of uninitialized value in hash element at x line 14.
Use of uninitialized value in hash element at x line 14.
$hash{$x} = 123; $hash{$y} = 123
Keys of $hash => $VAR1 = [
''
];
s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
| [reply] [d/l] |
Re: undef as a key in a hash.
by TedYoung (Deacon) on Sep 25, 2007 at 18:34 UTC
|
I had to try this myself, because I couldn't believe it:
$a{undef} = 1;
print defined for keys %a;
# Output
1
# This doesn't work either:
%a = (undef, 1);
I wonder what he was thinking...
| [reply] [d/l] |
|
|
| [reply] [d/l] |
|
|
I wonder what he was thinking...
The hash-style usage of constant wasn't added until perl 5.7.1, and the defined $name check was around before that. Basically all of 5.7.0's code was kept intact and put in the for loop. The defined $name check was just accidentally (I presume) left inside the for loop.
Here's how it should look.
my $multiple = ref $_[0];
if ( $multiple ) {
...
} else {
unless (defined $_[0]) {
require Carp;
Carp::croak("Can't use undef as constant name");
}
$constants{+shift} = undef;
}
foreach my $name ( keys %constants ) {
...
}
Not that this matters much, because
use constant undef, 'x';
will fail anyway. You just get a slightly less clear error message.
lodin | [reply] [d/l] [select] |
Re: undef as a key in a hash.
by Eimi Metamorphoumai (Deacon) on Sep 25, 2007 at 18:40 UTC
|
Hash keys are always strings, so if you try to use undef it will be coerced to the empty string, with a warning ("Use of uninitialized value in hash element") if warnings are enabled. So it shouldn't actually be possible for that code to be called. | [reply] [d/l] [select] |
Re: undef as a key in a hash.
by kyle (Abbot) on Sep 25, 2007 at 18:48 UTC
|
use Test::More tests => 6;
my %h = ( undef() => 1 );
foreach my $key ( keys %h ) {
ok( defined $key, 'key is defined' );
is( $key, '', 'key is empty string' );
}
is_deeply( [ '' ], [ keys %h ], 'undef key' );
is_deeply( [ 1 ], [ values %h ], 'values works' );
is( $h{undef()}, 1, 'undef key accesses value' );
is( $h{''}, 1, 'empty string key accesses value' );
Even when I try to be rude and slip it in there, it won't go.
package UndefKey;
sub TIEHASH { bless [], __PACKAGE__ }
sub FIRSTKEY { undef }
sub NEXTKEY { undef }
sub FETCH { 1 }
package main;
#my %h = ( undef() => 1 );
tie my %h, 'UndefKey';
(Running the same tests as before, only the last two succeed, and the ones in the foreach do not run because keys returns an empty list.) | [reply] [d/l] [select] |
Re: undef as a key in a hash.
by bart (Canon) on Sep 26, 2007 at 12:16 UTC
|
I couldn't believe it, but that really is in the source of constant 1.05, the version that comes with perl 5.8.8 (and also in the unauthorized release constant 1.11). What a major think-o by the author.
No, it could never work. Hash keys are strings. undef is not a string. | [reply] |
Re: undef as a key in a hash.
by ursus (Acolyte) on Sep 25, 2007 at 22:08 UTC
|
I think the error message might be better written Can't use '' as a constant name. Or maybe if you try to name a constant 'undef' its key mutates into '', which this code then croaks on.
Or maybe this piece of code never gets hit.
| [reply] [d/l] [select] |
|
|
Nope. '' won't reach there. '' is defined. He'd have to use length instead of defined to check for ''.
| [reply] [d/l] [select] |
|
|
Or use it as a boolean. :-)
| [reply] |