在Perl中,如何创建其键来自给定数组的哈希?

时间:2008-09-18 19:08:10

标签: arrays perl hash

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

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

是否有一种简单的方法来执行此数组到哈希转换?理想情况下,它应具有足够的通用性,可以采用匿名数组并返回匿名哈希值。

14 个答案:

答案 0 :(得分:110)

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

它没有“@hash {@array} = ...”解决方案那么短,但那些需要哈希和数组已经在其他地方定义,而这个可以采用匿名数组并返回匿名散列。

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

答案 1 :(得分:41)

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

这是一个哈希切片,来自哈希值的列表,所以它得到了前面的list-y @。

来自the docs

  

如果您对使用原因感到困惑   而是在哈希切片上的'@'   一个'%',想起来就像这样。该   支架类型(方形或卷曲)   管理它是一个数组还是一个   看着哈希。在另一   手,领先符号('$'或'@')   在数组或哈希表示是否   你得到了一个奇异的价值   (标量)或复数(列表)。

答案 2 :(得分:35)

@hash{@keys} = undef;

这里使用@引用哈希的语法是哈希切片。我们基本上是说$hash{$keys[0]}$hash{$keys[1]}$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,而其他一些解决方案正在转动车轮来构建一个数组进入哈希值的值;完全浪费了精力)。

答案 3 :(得分:15)

请注意,如果输入if ( exists $hash{ key } )并不是太多工作(我更喜欢使用,因为感兴趣的事实上是关键的存在而不是其价值的真实性),那么你可以使用短而甜蜜的

@hash{@key} = ();

答案 4 :(得分:7)

这里有一个预设,最有效的方法是做很多“数组是否包含X?”检查是将数组转换为哈希值。效率取决于稀缺资源,通常是时间,但有时是空间,有时是程序员的努力。通过同时保持列表和列表的散列,您至少可以将占用的内存增加一倍。另外,您正在编写更多原始代码,您需要对其进行测试,记录等。

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

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

我进行了快速测试,将/ usr / share / dict / words的一半加载到一个数组(25000个单词)中,然后在数组中查找从整个字典(每5000个单词)中选择的11个单词,数组到哈希方法和List :: MoreUtils中的any()函数。

在从源构建的Perl 5.8.8上,数组到散列方法的运行速度比any()方法快近1100倍(在Ubuntu 6.06打包的Perl 5.8.7下快了1300倍。)

然而,这不是完整的故事 - 数组到哈希转换大约需要0.04秒,在这种情况下,数组到哈希方法的时间效率会比any()方法快1.5到2倍。仍然很好,但不是那么出色。

我的直觉是,在大多数情况下,数组到哈希方法将胜过any(),但如果我有一些更可靠的指标(大量测试用例,我会感觉好多了)体面的统计分析,可能是对每种方法的一些大O算法分析等。)根据您的需要,List :: MoreUtils可能是更好的解决方案;它当然更灵活,需要更少的编码。请记住,过早优化是一种罪恶......:)

答案 5 :(得分:7)

我一直以为

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

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

答案 6 :(得分:5)

在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

答案 7 :(得分:4)

另外值得注意的是完整性,我通常用2个相同长度的数组@keys@vals执行此操作的方法是哈希...

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

答案 8 :(得分:3)

Raldi的解决方案可以收紧(原件中'=>'不是必需的):

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

此技术还可用于将文本列表转换为哈希值:

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

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

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

[编辑包括]


提供的另一种解决方案(需要两行)是:

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;

答案 9 :(得分:2)

您也可以使用Perl6::Junction

use Perl6::Junction qw'any';

my @arr = ( 1, 2, 3 );

if( any(@arr) == 1 ){ ... }

答案 10 :(得分:1)

如果你做了很多集理论操作 - 你也可以使用Set::Scalar或类似的模块。然后$s = Set::Scalar->new( @array )将为您构建设置 - 您可以使用以下内容进行查询:$s->contains($m)

答案 11 :(得分:1)

如果您不想污染您的命名空间,可以将代码放入子例程中。

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;

答案 12 :(得分:0)

您可能还想查看实现有序关联数组的Tie::IxHash。这将允许您在数据的一个副本上执行两种类型的查找(哈希和索引)。

答案 13 :(得分:0)

#!/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'
        };