假设我有一个数组,我知道我会做很多"数组是否包含X?" 检查.执行此操作的有效方法是将该数组转换为哈希,其中键是数组的元素,然后您可以说
if($hash{X}) { ... }
是否有一种简单的方法来执行此数组到哈希转换?理想情况下,它应该足够通用以获取匿名数组并返回匿名哈希.
%hash = map { $_ => 1 } @array;
它不像"@hash {@array} = ..."解决方案那么简短,但那些需要哈希和数组已经在其他地方定义,而这个可以采用匿名数组并返回匿名哈希.
这样做是将数组中的每个元素与"1"配对.当(key,1,key,1,key 1)对的这个列表被分配给散列时,奇数编号的那些成为散列的密钥,偶数编号的成为散列的值.
@hash{@array} = (1) x @array;
它是一个哈希切片,来自哈希值的列表,因此它在前面得到list-y @.
来自文档:
如果你对为什么在哈希切片而不是'%'上使用'@'感到困惑,那就这样想吧.括号的类型(方形或卷曲)决定它是一个数组还是正在查看的哈希.另一方面,数组或散列上的前导符号('$'或'@')表示您是返回单数值(标量值)还是复数值(列表).
@hash{@keys} = undef;
这里使用@
a表示散列的语法是散列片.我们基本上说$hash{$keys[0]}
AND $hash{$keys[1]}
AND $hash{$keys[2]}
...是一个左侧的列表,一个左值,我们分配给该列表,它实际上进入哈希并设置所有命名键的值.在这种情况下,我只指定了一个值,因此值进入$hash{$keys[0]}
,其他哈希条目都使用未定义的值自动生成(生命).[我在这里的原始建议设置了表达式= 1,这将把一个键设置为1,其他键设置为undef
.我改变它是为了保持一致,但正如我们将在下面看到的那样,确切的值无关紧要.
当你意识到左边的表达式(=左边的表达式)是一个由哈希构建的列表,那么它将开始理解为什么我们使用它@
.[除了我认为这将在Perl 6中发生变化.]
这里的想法是你使用哈希作为集合.重要的不是我指定的价值; 它只是键的存在.所以你想做的不是:
if ($hash{$key} == 1) # then key is in the hash
代替:
if (exists $hash{$key}) # then key is in the set
实际上运行exists
检查比使用散列中的值更有效,尽管对我来说重要的是你只是用散列的键表示一个集合的概念.此外,有人指出,通过undef
在此处使用值,我们将比分配值消耗更少的存储空间.(并且还产生较少的混淆,因为值无关紧要,我的解决方案只会为哈希中的第一个元素分配一个值而离开其他元素undef
,而其他一些解决方案正在转动车轮来构建一个值数组以进入哈希;完全浪费精力).
请注意,如果输入if ( exists $hash{ key } )
对你来说不是太多的工作(我更喜欢使用它,因为感兴趣的事实上是关键的存在而不是其价值的真实性),那么你可以使用短而甜的
@hash{@key} = ();
这里有一个预设,最有效的方法是做很多"阵列是否包含X?" 检查是将数组转换为哈希值.效率取决于稀缺资源,通常是时间,但有时是空间,有时是程序员的努力.通过同时保持列表和列表的散列,您至少可以将占用的内存增加一倍.另外,您正在编写更多原始代码,您需要对其进行测试,记录等.
作为替代方案,看看一览:: MoreUtils模块,具体的功能any()
,none()
,true()
和false()
.它们都将一个块作为条件,一个列表作为参数,类似于map()
和grep()
:
print "At least one value undefined" if any { !defined($_) } @list;
我运行了一个快速测试,将/ usr/share/dict/words的一半加载到一个数组(25000个单词),然后查找数组中整个字典(每个第5000个单词)中选择的11个单词,同时使用数组-to-hash方法和any()
List :: MoreUtils中的函数.
在从源构建的Perl 5.8.8中,数组到散列方法的运行速度比该any()
方法快近1100倍(在Ubuntu 6.06打包的Perl 5.8.7下快了1300倍.)
然而,这不是完整的故事 - 数组到哈希转换大约需要0.04秒,在这种情况下,数组到哈希方法的时间效率会比any()
方法快1.5倍-2倍.仍然很好,但不是那么出色.
我的直觉是any()
在大多数情况下阵列到哈希方法都会被击败,但如果我有一些更可靠的指标(大量的测试用例,不错的统计分析,可能是一些大的话),我会感觉好多了.每种方法的算法分析等.根据您的需要,List :: MoreUtils可能是更好的解决方案; 它当然更灵活,需要更少的编码.记住,过早优化是一种罪恶...... :)
我一直以为
foreach my $item (@array) { $hash{$item} = 1 }
至少很好,可读/可维护.
在perl 5.10中,有一个接近魔术的~~运算符:
sub invite_in { my $vampires = [ qw(Angel Darla Spike Drusilla) ]; return ($_[0] ~~ $vampires) ? 0 : 1 ; }
见这里:http://dev.perl.org/perl5/news/2007/perl-5.10.0.html
同样值得一提的是完整性,我通常使用2个相同长度的数组来执行此操作的方法@keys
,@vals
而您更希望使用的是哈希值...
my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);