Bug#940922: perl: iterating hash with "each", no write to hash but references got messed up
Leszek Dubiel
leszek.dubiel at dubielvitrum.pl
Sun Sep 22 00:39:30 BST 2019
Package: perl
Version: 5.24.1-3+deb9u5
Severity: normal
I was trying to make program below as simple as possible, but I don't understand where is the bug introduced.
The task is to make a copy of hash such that keys in new hash are made only out of alphanumerics and underscores "_". Hashes are nested.
Here is the program:
#!/usr/bin/perl
use Data::Dumper;
sub hash_is_okey {
ref $_[0] eq "HASH" or return 0;
# for my $k (keys %{$_[0]}) {
# my $v = ${$_[0]}{$k};
while (my ($k, $v) = each %{$_[0]}) {
$k =~ /\A[_[:alnum:]]+\z/ or return 0;
hash_ok_or_scalar($v) or return 0;
}
return 1;
}
sub hash_ok_or_scalar { return hash_is_okey($_[0]) || ! ref $_[0]; }
sub make_hash_with_good_keys {
my ($x) = $_[0];
hash_ok_or_scalar($x) and return $x;
ref $x eq "HASH" or die;
# copy hash "%{$x}" to new hash "%h" but make names good
my %h;
for my $k (keys %$x) {
# copy "$k" to "$n", then make good name of "$n"
my $n = $k =~ s/[^[:alnum:]]/_/gr;
$h{$n} = make_hash_with_good_keys($$x{$k});
}
return \%h;
}
my $original = { "top_level" => { "level down" => 123 } };
my $with_good_keys = make_hash_with_good_keys($original);
print Dumper($original, $with_good_keys);
and here is the result:
$VAR1 = {
'top_level' => {
'level down' => 123
}
};
$VAR2 = {
'top_level' => $VAR1->{'top_level'}
};
In program hash "$with_good_keys" is made by:
* initializing new hash "%h", and then asigning to that hash new value
or
* plugging reference if "subhash" has good keys.
But the result is bad, because "VAR2" has bad key "level down" -- contains space.
If I change this part of program:
# for my $k (keys %{$_[0]}) {
# my $v = ${$_[0]}{$k};
while (my ($k, $v) = each %{$_[0]}) {
to look like this (uncomment "for...keys", comment "while...each"):
for my $k (keys %{$_[0]}) {
my $v = ${$_[0]}{$k};
# while (my ($k, $v) = each %{$_[0]}) {
then result is:
$VAR1 = {
'top_level' => {
'level down' => 123
}
};
$VAR2 = {
'top_level' => {
'level_down' => 123
}
};
Now "$VAR2" is a copy with names modified as expected.
This is extremely strange, because I don't write to hash I am iterating over with "while...each". I tried to
resolve this myself for many hours, but I failed.
-- System Information:
Debian Release: 9.11
APT prefers oldstable-updates
APT policy: (500, 'oldstable-updates'), (500, 'oldstable')
Architecture: amd64 (x86_64)
Kernel: Linux 4.9.0-9-amd64 (SMP w/4 CPU cores)
Locale: LANG=pl_PL.UTF-8, LC_CTYPE=pl_PL.UTF-8 (charmap=UTF-8), LANGUAGE=pl_PL.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
Versions of packages perl depends on:
ii dpkg 1.18.25
ii libperl5.24 5.24.1-3+deb9u5
ii perl-base 5.24.1-3+deb9u5
ii perl-modules-5.24 5.24.1-3+deb9u5
Versions of packages perl recommends:
ii netbase 5.4
pn rename <none>
Versions of packages perl suggests:
pn libterm-readline-gnu-perl | libterm-readline-perl-perl <none>
pn make <none>
pn perl-doc <none>
-- no debconf information
More information about the Perl-maintainers
mailing list