为真理或存在检查Perl哈希键是否更好?

时间:2009-09-02 23:42:26

标签: perl hash

当分配给只是键的哈希(其中值不是真正需要的)时,它是否更优先,说:

$hash{$new_key} = "";

或者说:

$hash{$new_key} = 1;

一个人必须检查exists的密钥,另一个允许你说:

if (exists $hash{$some_key})

if ($hash{$some_key})

我认为分配1会更好,但这有什么问题吗?它甚至重要吗?

8 个答案:

答案 0 :(得分:16)

这取决于您是否需要密钥才能存在或具有真正的价值。测试你需要的东西。如果你只是使用哈希来查看列表中是否有东西,那么exists()就是你要走的路。如果你正在做其他事情,那么检查价值可能就好了。

答案 1 :(得分:11)

当不需要这些值时,你会经常看到这个成语:

my %exists;
$exists{$_}++ for @list;

具有将其设置为1的效果。

答案 2 :(得分:9)

如果您正在尝试保存内存(通常只有在您拥有非常大哈希值时才重要),您可以使用undef作为值并仅测试其存在。 Undef实现为单例,因此数千个undef都只是指向同一个值的指针。将每个值设置为空字符串或1将为每个元素分配不同的标量值。

my %exists;
@exists{@list} = ();

鉴于您后来对您的预期用途的评论,这是我多次见过和使用过的习语:

my %seen;
while (<>) {
    next if $seen{$_}++; # false the first time, true every successive time
    ...process line...
}

答案 3 :(得分:3)

假设您确实需要检查密钥的存在,但您编写的代码可以检查真相。它会在各个地方检查整个程序的真实性。然后它突然出现你误解了一些东西,你应该实际存储从你的键到字符串值的映射;字符串应该在您已经实现的相同数据流中使用。

字符串可以是空的!

因此,您应该重构您的程序或创建另一个哈希,因为真值检查不再检查存在。如果你从一开始就检查存在就不会发生这种情况。

(编辑coz dunno为什么会被拒绝。)

答案 4 :(得分:3)

*更新:* 思南指出,我对哈希元素创建的谨慎态度是过时的,而不是新的Perls问题。我在下面编辑了我的帖子,并对此事添加了一些新想法。

只测试真相的问题是你可以使用我在上学到的旧版Perl来修改哈希。使用Perl 5.8,这段代码是安全的:

my %foo = ();

if( $foo{bar} ) {
   print "never happens";
}

print keys %foo;

这是自动生活混合祝福的坏部分(我喜欢自动生活,但这就是它受伤的地方)。

在许多情况下,这没什么大不了的。但这是一个潜在的问题需要注意。我在我的代码中通过locking解决了这个必须保持未修改的任何哈希。

在实践中,我要么总是在布尔测试之前进行存在测试。

if( exists $foo{bar} and $foo{bar} ) {
    # hash is not modified due to short circuit
}

数组可能会发生同样的数据结构更改。如果您访问$foo[2000],则数组将被扩展。因此,在意外扩展数组之前测试存在是个好主意。在实践中,这比相应的哈希行为要小得多。&lt; - 具有讽刺意味的是,你可以 只在perls 5.6及更新版本的数组上使用,大概这个问题已得到解决。

如果我需要深入挖掘数据结构,我会使用Data::Diver。它会自动检查结构中每个级别的存在,以防止意外更改数据结构。

最重要的是在每个脚本/程序中保持一致。遇到问题的最简单方法是在这里测试存在,但在那里测试真相。特别是如果您为两组测试访问相同的哈希值。

关于自动更新的最新消息的最终想法:一系列研究表明了一些事情。我应该在发布之前测试我的代码 - 由于没有这样做,我传播了错误的信息,我为此道歉。我还发现仍有some sneaky issues with autovivification徘徊 - 足够有open todo item to make things right。因此,虽然它可能是错误的,老式的和愚蠢的,但我将继续明确地采取措施来控制自动生成并将其限制为当我希望它发生时。 FWIW,autivivification在工作时是一件好事。我认为防止autoviv的特殊套管if是正确的做法 - 它摆脱了对大量额外代码的需求,但我希望我能找到一些详细说明这种行为的文档。

答案 5 :(得分:2)

正如先前的回答所说,这取决于你想要实现的目标;如果你只是试图从某个集合中获取(例如)唯一值(其元素然后形成键),则可以使用exists(如果在分配值之前先检查存在,也可以帮助捕获重复值)。 / p>

在不了解应用程序的情况下,很难更具体。

答案 6 :(得分:2)

答案 7 :(得分:0)

我通常检查defined值。这是你要离开的中间案例。不完全“真相”也不完全“存在”。 (大部分,但不完全。)

现在理论上,一般方式越exists,如

if ( exists $hash{$key} ) return 'strawberry';

这包括密钥存在且值为0的情况,或者密钥已分配undef的情况。密钥只需存在即可通过此测试。

但是,我很少发现需要测试密钥的存在。

  1. 哈希通常是已定义API的一部分,如果您正在处理它们,您可以了解可以存储的值范围。配置项将寻找特定的东西;作为无序参数键,子程序将寻找特定的东西。

  2. 我发现“无限表”是一个非常灵活的概念。 exists x&lt; =&gt; defined x适用于此。每个可以想象的值都在表中“设置”,但只有有限数量的键定义,其余的被认为是未定义的。

    因此,通常,除非在散列中定义了值,否则我不在乎它是什么。我认为这是假的价值。在我写的大多数事情中,存储undef而不存储任何东西都是等价的。以下项目进一步推动了这一点。

  3. 大部分时间我可能需要知道密钥是否在表中,我需要将其用于其他方面。首先,我将值存储在本地,然后测试是否为定义的值。

     my $value = $hash{$key};
     if ( defined $value ) { 
         push @valid_values, $value;
     }
    

    如果我可以确定在exists的查找和使用该值的查找之间存在一些本地公共子表达式优化,那么我就不会那么挑剔了。但我不想多次从哈希中检索。所以我1)缓存值并且2)每次检查它。

  4. 也就是说,我可以收紧标准我知道该值不应该是0,例如在查找或参数表中。所以我有时会测试真相。但无论如何,我也可以收紧任何测试。

         if ( ( $hash{$key} || '' ) =~ m/^(?:Bears|Lions|Packers|Vikings)$/ ) { 
             $nfc_north++;
         }
    
    1. 当然,这里的操作原则是defined适用于“无限制”表。其中每个可以想象的值都在表中“设置”,但只有有限数量的键被定义
    2. 有一种情况是您可能正在使用完全匿名的哈希。但是,您对keysvalues无法满足的键的兴趣是什么?即使您正在制作一个通用哈希“便利功能”,最好不要将自己存在于特定键中,以便对其他人存储的内容完全保持中立。