我之前收到的一个问题是来自着名的Perl专家,Perl作者和Perl培训师brian d foy的following response:
[If]你在每个文件名的末尾寻找一个固定的字符序列。您想知道该固定序列是否在您感兴趣的序列列表中。将所有扩展存储在哈希中并查看该哈希:my( $extension ) = $filename =~ m/\.([^.]+)$/; if( exists $hash{$extension} ) { ... }您不需要构建正则表达式,也不需要经历几个可能的正则表达式替换来检查您必须检查的每个扩展。
感谢brian的建议。
我现在想知道的是在上述案例中最佳做法是什么。应该只定义键,这是我实现上述内容所需要的,还是应该总是定义一个值?
答案 0 :(得分:5)
通常最好为每个键设置一个定义的值。惯用价值(当你不关心价值时)是1。
my %hash = map { $_ => 1 } @array;
这样做会使得使用哈希的代码稍微简单一些,因为您可以使用$hash{key}
作为布尔值。如果值未定义,则需要使用更详细的exists $hash{key}
代替。
也就是说,有些情况下需要undef
的值。例如:假设您正在解析C头文件以提取预处理器符号。将这些存储在名称=>的散列中是合乎逻辑的。价值对。
#define FOO 1
#define BAR
在Perl中,这将映射到:
my %symbols = ( FOO => 1, BAR => undef);
在C中#define
定义符号,而不是值 - 在C中“已定义”在Perl中映射为“exists”。
答案 1 :(得分:4)
如果没有值,则无法创建哈希键。值可以是undef但它会在那里。你怎么会构造一个哈希。或者你的问题是关于价值是否可以取消?在这种情况下,我会说你存储的值(undef,1,0 ......)完全取决于你。如果很多人都在使用它,那么你可能想要存储一些真正的值,尽管其他人使用if($ hash {$ extension}){...}而不是存在因为他们没有注意。
答案 2 :(得分:3)
undef
是一个值。
当然,像你这样的东西总是对你目前正在做的事情感到沮丧。但是$foo{bar}
只是一个像$bar
这样的变量,我没有看到任何一个不应该偶尔undef
的原因。
PS:
这就是exists
存在的原因。
答案 3 :(得分:3)
正如其他人所说,哈希集的惯用解决方案(只包含键而不是值的哈希)是使用1作为值,因为这使得存在的测试变得容易。但是,使用undef
作为值有一些说法。它会强制用户使用exists
来测试是否存在,这会稍快一些。当然,即使值为1,您也可以使用exists
测试是否存在,并避免忘记使用exists
的用户不可避免的错误。
答案 4 :(得分:1)
我知道使用Considered Harmful is considered harmful,但这很糟糕;几乎与无限制的goto
用法一样糟糕。
好的,我在一些评论中已经对此有所帮助,但我认为我需要一个完整的回复来证明这个问题。
假设我们有一个守护进程,为销售小部件的商店提供后端库存控制。
my @items = qw(
widget
thingy
whozit
whatsit
);
my @items_in_stock = qw(
widget
thingy
);
my %in_stock;
my @in_stock(@items_in_stock) = (1) x @items_in_stock; #initialize all keys to 1
sub Process_Request {
my $request = shift;
if( $request eq REORDER ) {
Reorder_Items(\@items, \%in_stock);
}
else {
Error_Response( ILLEGAL_REQUEST );
}
}
sub Reorder_Items{
my $items = shift;
my $in_stock = shift;
# Order items we do not have in-stock.
for my $item ( @$items ) {
Reorder_Item( $item )
if not exists $in_stock->{$item};
}
}
该工具很棒,它可以自动保存库存物品。非常好。现在,老板要求自动生成库存商品的目录。因此,我们修改Process_Request()
并添加目录生成。
sub Process_Request {
my $request = shift;
if( $request eq REORDER ) {
Reorder_Items(\@items, \%in_stock);
}
if( $request eq CATALOG ) {
Build_Catalog(\@items, \%in_stock);
}
else {
Error_Response( ILLEGAL_REQUEST );
}
}
sub Build_Catalog {
my $items = shift;
my $in_stock = shift;
my $catalog_response = '';
foreach my $item ( @$items ) {
$catalog_response .= Catalog_Item($item)
if $in_stock->{$item};
}
return $catalog_response;
}
在测试Build_Catalog()时工作正常。万岁,我们使用该应用程序。
糟糕。由于某些原因,没有订购任何东西,该公司缺少所有的库存。
Build_Catalog()
例程会为%in_stock
添加密钥,因此Reorder_Items()
现在可以查看库存中的所有内容,但从不订购。
使用Hash::Util的lock_hash
可以帮助防止意外的哈希修改。如果我们在调用'Build_Catalog()之前锁定了%in_stock
,那么我们就会遇到一个致命的错误,并且永远不会使用该bug。
总之,最好测试密钥的存在而不是set-hash值的真实性。如果您使用存在作为能指,请不要将值设置为“1”,因为这会掩盖错误并使其更难跟踪。使用lock_hash
可以帮助解决这些问题。
如果必须检查值的真实性,请在 每个 情况下执行此操作。
答案 5 :(得分:1)
使用undef作为哈希值比存储1更节省内存。