当分配给一个仅包含键的哈希表(其中值并不是真正需要的)时,更倾向于使用哪种方式:
$hash{$new_key} = "";
或者说:
$hash{$new_key} = 1;
其中一个必须使用 exists
检查键是否存在,而另一个则允许你使用以下任意一种形式:
if (exists $hash{$some_key})
或者if ($hash{$some_key})
我认为赋值1可能更好,但这样做会有什么问题吗?这真的很重要吗?
当分配给一个仅包含键的哈希表(其中值并不是真正需要的)时,更倾向于使用哪种方式:
$hash{$new_key} = "";
或者说:
$hash{$new_key} = 1;
其中一个必须使用 exists
检查键是否存在,而另一个则允许你使用以下任意一种形式:
if (exists $hash{$some_key})
或者if ($hash{$some_key})
我认为赋值1可能更好,但这样做会有什么问题吗?这真的很重要吗?
这取决于您需要键是否存在或具有真实值。测试您需要的东西。如果您仅使用哈希来查看某些内容是否在列表中,则exists()是正确的方法。如果您正在执行其他操作,则检查值可能是正确的方法。
当不需要这些值时,你经常会看到这种用法:
my %exists;
$exists{$_}++ for @list;
这将导致它被设置为1。
use strict;
的情况下产生警告,抱怨你在任何键的第一个增加操作中操纵未定义的变量。 - j_random_hackermy %exists; @exists{@list} = ();
或者这个习语: my %seen; ... 除非($seen {$item}++),否则 {打印“第一次为$item”}
- ysth如果你想节省内存(通常只有在使用非常大的哈希表时才会关注内存),你可以将值设置为undef并测试其是否存在。Undef被实现为单例,因此成千上万个undef都只是指向同一个值的指针。将每个值设置为空字符串或1将为每个元素分配不同的标量值。
my %exists;
@exists{@list} = ();
考虑到你后来的评论提到了你的预期使用情况,这是我多次看到并使用的习语:
my %seen;
while (<>) {
next if $seen{$_}++; # false the first time, true every successive time
...process line...
}
()
和 undef
是一样的吗? - Joe* 更新: * Sinan 指出,我的谨慎方法对哈希元素的创建已经过时,并且在更新版本的 Perl 中不是问题。我已经编辑了下面的文章,并对此发表了一些新的想法。
仅测试真实性的问题在于,您可以使用我学习的老旧版本的 Perl 修改哈希。这段代码在 Perl 5.8 中是安全的:
my %foo = ();
if( $foo{bar} ) {
print "never happens";
}
print keys %foo;
if( exists $foo{bar} and $foo{bar} ) {
# hash is not modified due to short circuit
}
数组也可能出现数据结构的改变。如果您访问<-- 讽刺的是,只有在perls 5.6及更高版本中才能在数组上使用exists,这里可能已经修复了这个问题。$foo[2000]
,则数组将被扩展。因此,在意外扩展数组之前测试其是否存在可能是一个好主意。实际上,这比相应的哈希行为要少得多。
如果我需要深入挖掘数据结构,我会使用Data::Diver。它会自动检查结构中每个级别的存在性,以防止意外修改数据结构。
最重要的是在每个脚本/程序中保持一致。遇到问题的最简单方法是在这里测试存在性,但在那里进行真相测试。特别是如果您访问相同的哈希表进行两组测试。
关于自动创建数据结构更新的最终想法: 一系列的研究显示了几个问题。在发布之前我应该测试我的代码,由于没有这样做,我传播了错误信息,对此我深表歉意。我也发现仍然有 一些难以察觉的自动创建数据结构问题,足以让我们需要一个 开放式待办事项来解决。所以,即使它可能是错误的、老式的和愚蠢的,我将继续明确地采取措施来控制自动创建数据结构并将其限制在我想要它发生的时候 只有 发生。顺便说一下,当自动创建数据结构起作用时,它是一件好事。我认为特别针对if
的情况以防止自动创建数据结构是正确的事情 - 它消除了大量额外的代码需求,但我希望能找到一些详细说明这种行为的文档。if ($foo{bar}{baz}{qux})
)将自动创建除最底层之外的所有级别(也就是说,在这种情况下,$foo{bar}{baz}
将被创建)。 - j_random_hacker正如之前的回答所说,这取决于您想要实现什么;如果您只是想从某个集合中获取(例如)唯一值(其元素然后形成键),则可以使用exists(如果在分配值之前首先检查exists,则还可以帮助捕获重复项)。
不知道应用程序的情况下,更具体的描述就比较困难了。
我通常检查defined
值。这是你忽略的中间情况。不完全是“真实”的,也不完全是“存在”的。(大多数情况下是如此,但并非完全如此。)
现在理论上,更加通用的方式是exists
,例如
if ( exists $hash{$key} ) return 'strawberry';
这种情况适用于键存在且值为0
,或者键已被分配为undef
。键只需要存在即可通过此测试。
然而,我很少发现需要测试键的存在性。
哈希常常是API的一部分,如果你正在处理它们,你对可以存储的值的范围有一些了解。配置项将寻找特定的内容;作为无序参数键,子程序将寻找特定的内容。
我认为“无限表”的概念非常灵活。而且exists x
<=> defined x
对此很有效。在表中,每个可想象的值都被“设置”,但只有有限数量的键被定义,其余的被视为未定义。
因此,通常情况下,除非哈希中定义了一个值,否则我不关心它是什么。我认为它是一个假值。在我编写的大多数代码中,存储undef
和不存储任何东西是等效的。这进一步激励了下面的项目。
大多数时候,我需要知道一个键是否在表中,我需要用它来做其他事情。首先,我将值存储在本地,然后测试它是否为已定义的值。
my $value = $hash{$key};
if ( defined $value ) {
push @valid_values, $value;
}
如果我能确信在查找exists
和查找使用值之间有一些本地常见子表达式优化,那么我就不会这么挑剔了。但我不喜欢从哈希中检索超过一次。所以我1)缓存值并2)每次都检查它。
话虽如此,如果我知道该值不应为0
,例如在查找或参数表中,我可以收紧标准。因此,有时我会测试真实性。但我也可以随意收紧任何测试。
if ( ( $hash{$key} || '' ) =~ m/^(?:Bears|Lions|Packers|Vikings)$/ ) {
$nfc_north++;
}
defined
适用于“无限”的表。在表中设置了每个可想象的值,但只有有限数量的键被定义。keys
或values
满足的键有什么兴趣呢?即使你正在制作一个通用哈希“方便函数”,最好不要关注特定键的存在,以完全中立的方式对待其他人存储的内容。