在Perl中,我如何创build一个哈希键来自给定的数组?

比方说,我有一个数组,我知道我会做很多“数组是否包含X? 检查。 这样做的有效方法是将该数组转换为哈希,其中键是数组的元素,然后您可以说

  if($ hash {X}){...} 

有一个简单的方法来做这个数组到哈希转换? 理想情况下,它应该是足够多function的采取一个匿名数组并返回一个匿名散列。

%hash = map { $_ => 1 } @array; 

它不像“@hash {@array} = …”解决scheme那样简短,但是那些需要散列和数组已经被定义在别的地方,而这个可以使用一个匿名数组并且返回一个匿名散列。

这样做是将数组中的每个元素与一个“1”配对。 当(密钥,1,密钥,1,密钥1)对的这个列表被分配给一个散列值时,奇数值的那个成为散列的密钥,而偶数的那个成为相应的值。

  @hash{@array} = (1) x @array; 

这是一个散列片段,是散列值的列表,所以它在前面得到list-y @。

从文档 :

如果你对为什么在一个散列片上使用'@'而不是'%'感到困惑,可以这样想。 括号(正方形或curl)的types决定着它是一个数组还是一个正在查看的哈希。 另一方面,数组或哈希上的前导符号('$'或'@')表示您是返回单数值(标量)还是复数(列表)。

 @hash{@keys} = undef; 

这里你用@来表示散列的语法是散列片。 我们基本上是说$hash{$keys[0]} AND $hash{$keys[1]} AND $hash{$keys[2]} …是=左边的一个列表,左值,我们正在分配给那个实际进入散列的列表,并设置所有指定键的值。 在这种情况下,我只指定了一个值,使得值进入$hash{$keys[0]} ,而其他哈希项全部自动生成(生命),具有未定义的值。 [我原来的build议是设置expression式= 1,这将设置一个键1和其他undef 。 为了保持一致性,我改变了它,但正如我们下面所看到的那样,确切的数值并不重要。

当你意识到左边的expression式,是左侧的expression式,是一个由散列构成的列表,那么就可以开始理解为什么我们使用了@ 。 [除了我认为这将改变在Perl 6中。]

这里的想法是你使用散列作为一个集合。 重要的不是我分配的价值; 这只是钥匙的存在。 所以你想要做的不是这样的:

 if ($hash{$key} == 1) # then key is in the hash 

代替:

 if (exists $hash{$key}) # then key is in the set 

实际上,运行一个exists检查比在散列中的值更麻烦,但对于我来说,这里重要的只是你用一个散列键来表示一个集合的概念。 另外,有人指出,通过在这里使用undef作为值,我们将消耗比指定值更less的存储空间。 (也会产生较less的混淆,因为这个值并不重要,我的解决scheme将只为哈希中的第一个元素赋值,而将其他值undef ,其他一些解决scheme则将车轮转换为构build值的数组进入哈希;完全浪费的努力)。

注意,如果inputif ( exists $hash{ key } )对你来说不是太多工作(我更喜欢使用它,因为感兴趣的事情实际上是一个键的存在而不是它的价值的真实性),那么你可以使用短而甜

 @hash{@key} = (); 

这里有一个假设,那就是最有效的方法来做大量的“数组是否包含X? 检查是将数组转换为散列。 效率取决于稀缺资源,往往是时间,但有时候是空间,有时是程序员的努力。 通过同时保存列表和哈希表,您至less可以将内存翻一番。 另外,你正在写更多的原始代码,你需要testing,文档等。

作为替代,查看List :: MoreUtils模块,特别是函数any()none()true()false() 。 它们都以条件和一个列表作为参数,类似于map()grep()

print "At least one value undefined" if any { !defined($_) } @list;

我运行了一个快速testing,将一半的/ usr / share / dict / words加载到一个数组(25000个字),然后从数组中找出从整个字典中select的十一个单词(每第5000个单词) -to-hash方法和List :: MoreUtils中的any()函数。

在从源代码构build的Perl 5.8.8上,array-to-hash方法的运行速度比any()方法快1100倍(在Ubuntu 6.06打包的Perl 5.8.7下快了1300倍)。

然而,这并不是完整的故事 – arrays到哈希的转换大约需要0.04秒,在这种情况下,arrays到哈希方法的时间效率比any()方法快1.5倍到2倍。 仍然不错,但不像恒星。

我的直觉是,在大多数情况下,数组到哈希方法将会击败any() ,但是如果我有更坚实的度量(大量的testing用例,体面的统计分析,可能会更好一些一些对每种方法的大Oalgorithm分析等等)根据你的需要,List :: MoreUtils可能是一个更好的解决scheme; 这当然更灵活,需要更less的编码。 请记住,过早的优化是一个罪… 🙂

我一直认为

 foreach my $item (@array) { $hash{$item} = 1 } 

至less很好,可读/可维护。

在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

你也可以使用Perl6 :: Junction 。

 use Perl6::Junction qw'any'; my @arr = ( 1, 2, 3 ); if( any(@arr) == 1 ){ ... } 

拉尔迪的解决scheme可以收紧到这一点(从原来的'=''是没有必要的):

 my %hash = map { $_,1 } @array; 

这种技术也可以用于将文本列表转换为哈希值:

 my %hash = map { $_,1 } split(",",$line) 

另外,如果你有一行这样的值:“foo = 1,bar = 2,baz = 3”,你可以这样做:

 my %hash = map { split("=",$_) } split(",",$line); 

[编辑包括]


提供的另一个解决scheme(需要两行)是:

 my %hash; #The values in %hash can only be accessed by doing exists($hash{$key}) #The assignment only works with '= undef;' and will not work properly with '= 1;' #if you do '= 1;' only the hash key of $array[0] will be set to 1; @hash{@array} = undef; 

另外值得注意的是完整性,我常用的方法是使用2个相同长度的数组@keys@vals ,而您更喜欢使用哈希。

my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);

如果你做了很多理论上的操作 – 你也可以使用Set :: Scalar或类似的模块。 然后$s = Set::Scalar->new( @array )会为你build立Set,你可以用$s->contains($m)来查询它。

如果你不想污染你的名字空间,你可以把代码放到子程序中。

 my $hash_ref = sub{ my %hash; @hash{ @{[ qw'one two three' ]} } = undef; return \%hash; }->(); 

甚至更好:

 sub keylist(@){ my %hash; @hash{@_} = undef; return \%hash; } my $hash_ref = keylist qw'one two three'; # or my @key_list = qw'one two three'; my $hash_ref = keylist @key_list; 

如果你真的想传递一个数组引用:

 sub keylist(\@){ my %hash; @hash{ @{$_[0]} } = undef if @_; return \%hash; } my @key_list = qw'one two three'; my $hash_ref = keylist @key_list; 

你可能也想看看Tie :: IxHash ,它实现了有序的关联数组。 这将允许您在一个数据副本上执行两种types的查找(散列和索引)。

 #!/usr/bin/perl -w use strict; use Data::Dumper; my @a = qw(5 8 2 5 4 8 9); my @b = qw(7 6 5 4 3 2 1); my $h = {}; @{$h}{@a} = @b; print Dumper($h); 

给出(注意重复键获得数组中最大位置的值 – 即8> 2而不是6)

 $VAR1 = { '8' => '2', '4' => '3', '9' => '1', '2' => '5', '5' => '4' };