迭代时无意中将键添加到哈希
Posted
技术标签:
【中文标题】迭代时无意中将键添加到哈希【英文标题】:Unintentionally adding keys to hash while iterating 【发布时间】:2019-02-18 20:29:30 【问题描述】:我正在遍历指向经度/城市的键/值对的纬度键哈希值的缓存。我正在尝试找到与已经查找的内容足够接近并且在散列中的纬度/经度的近似匹配。
我就是这样做的
foreach my $lat_key ( keys $lookup_cache_latlonhash )
if ( ($lat > ($lat_key - .5)) && ($lat < ($lat_key + .5)) )
foreach my $lon_key ( keys % $lookup_cache_latlonhash->$lat_key )
if ( ($lon > ($lon_key - .5)) && ($lon < ($lon_key + .5)) )
$country = $$lookup_cache_latlonhash$lat_key$lon_key;
print "Approx match found: $lat_key $lon_key $country\n";
return $country;
代码用于在该范围内查找这些纬度/经度对。然而,对于它循环使用的每个纬度,当它确实发现它在范围内(第一个嵌套条件)时,它会将其添加到不打算使用的哈希(可能是keys % $goog_lookup_cache_latlonhash->$lat_key
),向哈希添加无用/空键:
$VAR1 =
'37.59' => ,
'37.84' => ,
'37.86' => ,
'37.42' =>
'126.44' => 'South Korea/Jung-gu'
,
'37.92' => ,
'37.81' => ,
'38.06' =>
'-122.53' => 'America/Novato'
,
'37.8' => ,
'37.99' => ,
'37.61' => ,
...
进行此查找的聪明或至少理智的方法是什么?所以我不会只是通过查找它们而无意中将键添加到哈希中?
【问题讨论】:
请注意,允许keys
处理引用被认为是一个坏主意,因此已从 Perl v5.24 中删除。所以keys $lookup_cache_latlonhash
应该写成keys %$lookup_cache_latlonhash
。
【参考方案1】:
您遇到的是auto-vivification。 Perl 的一个特性是让使用嵌套结构更容易一些。
任何时候取消引用未定义的值,perl 都会自动创建您正在访问的对象。
use Data::Dumper;
my $hash = ; if ($hash->'a') #No auto-vivification because you're just checking the value
keys %$hash->'b'; #auto-vivification because you're acting on the value (getting the keys of it) $hash->b
print Dumper($hash);
有几种方法可以避免这种情况 -
-
在你想避免这种情况的范围内添加
no autovivification
功能性
检查您访问的项目是否已定义
或存在(并且是您需要的类型)
我推荐第二个,因为它有助于养成检查代码是否正确数据结构的习惯,并使调试更加容易。
foreach my $lat_key (keys $lookup_cache_latlonhash)
if (($lat > ($lat_key - .5))
&& ($lat < ($lat_key + .5))
&& ref($lookup_cache_latlonhash->$lat_key) eq 'HASH') #expecting a hash here - undefined or any non-hash value will skip the foreach
foreach my $lon_key (keys % $lookup_cache_latlonhash->$lat_key)
if (($lon > ($lon_key - .5)) && ($lon < ($lon_key + .5)))
$country = $$lookup_cache_latlonhash$lat_key$lon_key;
print "Approx match found: $lat_key $lon_key $country\n";
return $country;
【讨论】:
非常有意义,并且每个 foreach 之间的夹层是明确的 - 彻底的答案。我想知道为什么no autovivification
不被 use strict
调用 - 感觉应该是,不是吗?
@ikebukuru - 根据我的经验,自动激活是 90% 以上的期望行为(或至少不是不期望的行为)。很多人第一次遇到它时确实会感到惊讶,但除此之外它造成任何伤害的情况极为罕见。
我不清楚为什么你认为keys % $lookup_cache_latlonhash->$lat_key
会在循环for my $lat_key ( keys $lookup_cache_latlonhash )
内自动激活一个元素,所以该元素必须已经存在。
@Borodin 确实!这就是为什么它让我摸不着头脑。感谢您清理我的问题代码以提高可读性。
@ikebukuru:我可以看到使用您自己的代码发生这种情况的唯一方法是,如果您的元素已经存在但值为undef
,它也将被自动激活。那可能吗?如果不是,那么问题必须存在于您的代码中的其他地方。检查外部 for
循环前后的哈希值。【参考方案2】:
放
no autovivification;
在范围内。
【讨论】:
【参考方案3】:您可以为此使用exists keyword。
解决方案
use Data::Dumper;
$hash = ;
$hash'alpha' = 'yep';
$hash'beta' = 'this too';
if (exists $hash'gamma')
print "Found gamma."
print Dumper(\%hash);
$hash'gamma' = 'added';
if (exists $hash'gamma')
print "Gamma was updated.\n"
print Dumper(\%hash);
示例输出
$VAR1 =
'beta' => 'this too',
'alpha' => 'yep'
;
Gamma was updated.
$VAR1 =
'gamma' => 'added',
'beta' => 'this too',
'alpha' => 'yep'
;
【讨论】:
这与问题中的代码完全无关。以上是关于迭代时无意中将键添加到哈希的主要内容,如果未能解决你的问题,请参考以下文章
RuntimeError:在 Rack 中的迭代期间无法将新密钥添加到哈希中