1if$hashX ... 有没有简单的方法"/>

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

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

In Perl, how do I create a hash whose keys come from a given array?

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

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

有没有简单的方法可以执行此数组到哈希的转换? 理想情况下,它应该足够通用以接受匿名数组并返回匿名哈希。


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

它不如" @hash {@array} = ..."解决方案那么短,但是那些解决方案要求哈希和数组已经在其他地方定义,而该解决方案可以采用匿名数组并返回匿名哈希。

这是将数组中的每个元素都与" 1"配对。当此(键,1,键,1,键1)对的列表分配给哈希时,奇数的键成为哈希的键,而偶数的键成为各自的值。


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

这是一个散列片,是散列中的值的列表,因此它的前面是列表y @。

从文档:

If you're confused about why you use
an '@' there on a hash slice instead
of a '%', think of it like this. The
type of bracket (square or curly)
governs whether it's an array or a
hash being looked at. On the other
hand, the leading symbol ('$' or '@')
on the array or hash indicates whether
you are getting back a singular value
(a scalar) or a plural one (a list).


1
@hash{@keys} = undef;

在这里使用@引用哈希的语法是哈希切片。我们基本上是说$hash{$keys[0]} AND $hash{$keys[1]} AND $hash{$keys[2]} ...是=左侧的一个列表,即一个左值,我们正在分配给该列表,该列表实际上进入了哈希并设置所有命名键的值。在这种情况下,我仅指定一个值,以便该值进入$hash{$keys[0]},而其他哈希条目都使用未定义的值自动生存(生效)。 [我最初的建议是将表达式设置为1,这会将一个键设置为1,将另一个键设置为undef。为了保持一致性,我对其进行了更改,但是正如我们将在下面看到的,确切的值无关紧要。]

当您意识到左值(即=左侧的表达式)是从哈希表中构建出来的列表时,那么就会开始理解为什么我们使用该@。 [除了我认为这会在Perl 6中改变。]

这里的想法是您使用哈希作为集合。重要的不是我分配的值;只是钥匙的存在。因此,您要做的不是:

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

代替:

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

实际上,仅运行exists检查要比打乱散列中的值更有效,尽管对我而言,重要的只是用散列键表示集合的概念。另外,有人指出,通过在此处使用undef作为值,我们将比分配值消耗更少的存储空间。 (并且也减少了混乱,因为该值无关紧要,而且我的解决方案将只为哈希中的第一个元素分配一个值,而将其他值保留为undef,而其他一些解决方案则使车轮转向构建值的数组进入哈希表;完全浪费了精力)。


请注意,如果键入if ( exists $hash{ key } )不太适合您(我宁愿使用它,因为感兴趣的问题实际上是键的存在而不是键值的真实性),那么您可以使用简短易懂的键

1
@hash{@key} = ();


我一直以为

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

至少很好并且可读/可维护。


这里有一个前提,即做很多"数组是否包含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个单词,同时使用这两个数组-to-hash方法和List :: MoreUtils中的any()函数。

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

但是,这还不是全部内容-数组到哈希的转换大约需要0.04秒,在这种情况下,数组到哈希方法的时间效率比any()方法快1.5到2倍。仍然不错,但不及恒星。

我的直觉是,在大多数情况下,数组到哈希方法将胜过any(),但是如果我有一些更可靠的指标(很多测试用例,不错的统计分析,也许每种方法都需要一些big-O算法分析,等等。)根据您的需求,List :: MoreUtils可能是一个更好的解决方案;它当然更加灵活,并且需要更少的编码。记住,过早的优化是一种罪过... :)


同样值得一提的是完整性,我使用2个相同长度的数组@keys@vals进行此操作的常用方法是一个哈希...

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


在perl 5.10中,有一个接近魔术的~~运算符:

1
2
3
4
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


Raldi的解决方案可以做到这一点(不需要原始的'=>'):

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

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

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

此外,如果您有一行这样的值:" foo = 1,bar = 2,baz = 3",则可以执行以下操作:

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

[编辑包括]

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

1
2
3
4
5
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;

您也可以使用Perl6 :: Junction。

1
2
3
4
5
use Perl6::Junction qw'any';

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

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

1
2
3
4
5
6
7
8
9
10
11
12
#!/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)

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

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

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

甚至更好:

1
2
3
4
5
6
7
8
9
10
11
12
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;

如果您真的想传递数组引用:

1
2
3
4
5
6
7
8
sub keylist(\\@){
  my %hash;
  @hash{ @{$_[0]} } = undef if @_;
  return \\%hash;
}

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

如果您进行了大量的设定理论运算-您也可以使用Set :: Scalar或类似的模块。然后$s = Set::Scalar->new( @array )将为您构建Set-您可以使用以下命令进行查询:$s->contains($m)


您可能还想签出Tie :: IxHash,它实现了有序的关联数组。这样一来,您就可以在数据的一个副本上进行两种类型的查找(哈希和索引)。


推荐阅读

    linux操作数据库命令?

    linux操作数据库命令?,地址,服务,系统,密码,数据库,工具,名字,首页,命令,参

    linux系命令的操作?

    linux系命令的操作?,工作,系统,信息,网络,命令,基础,简介,管理,目录,操作,lin

    linux执行多次命令?

    linux执行多次命令?,系统,信息,标准,工作,情况,命令,周期性,服务,代码,时间,l

    linux显示操作命令?

    linux显示操作命令?,系统,工作,地址,信息,管理,命令,目录,基础,工具,标准,lin

    linux并行化执行命令?

    linux并行化执行命令?,系统,工具,信息,命令,名称,网络,管理,首页,服务,暂停,L

    如何执行linux命令?

    如何执行linux命令?,单位,系统,网络,信息,权威,命令,文件,音乐,目录,选项,mv

    linux打断执行的命令?

    linux打断执行的命令?,系统,状态,网站,标准,通用,客服,人员,名字,网络,暂停,L

    检查linux服务器命令?

    检查linux服务器命令?,系统,信息,状态,情况,时间,工具,网络,环境,服务,电脑,

    linux操作常用命令?

    linux操作常用命令?,工作,系统,地址,信息,命令,目录,管理,标准,基础,工具,lin

    linux命令没执行完?

    linux命令没执行完?,系统,设备,工具,情况,密码,状态,电脑,管理,材料,服务,Lin

    linux操作命令重命名?

    linux操作命令重命名?,图片,软件,名称,名字,文件,命令,状态,代码,工具,系统,L

    shell中执行linux命令?

    shell中执行linux命令?,系统,名称,环境,管理,工作,代码,技术,软件,经理,基础

    linux打印命令执行?

    linux打印命令执行?,信息,系统,工具,服务,命令,发行,基础,位置,设备,时间,怎

    linux执行两次命令?

    linux执行两次命令?,系统,信息,连续,名称,命令,初级,首页,工具,管理,终端,lin

    linux命令执行安装?

    linux命令执行安装?,软件,系统,管理,网站,官网,市场,中心,最新,灵活,工作,如

    linux执行一条新命令?

    linux执行一条新命令?,系统,工作,命令,管理,网络,服务,信息,目录,路径,脚本,L

    linux目录操作命令d?

    linux目录操作命令d?,工作,系统,信息,命令,情况,基础,数据,名称,地址,目录,li

    监控linux执行命令?

    监控linux执行命令?,系统,情况,数据,实时,网络,信息,状态,时间,设备,命令,如

    linux初学者操作命令?

    linux初学者操作命令?,工作,系统,信息,命令,网络,地址,单位,位置,管理,数据,L

    linux操作命令远程?

    linux操作命令远程?,软件,密码,系统,名称,图片,电脑,地址,网站,工具,服务,如