使用另一个哈希值定义哈希值。

时间:2012-01-23 19:28:32

标签: perl perl-data-structures

在仅使用一种数据结构时,有没有办法执行以下操作?

my %hash = (
  "key1" => "value1",
  "key2" => "value2",
  "key3" => $hash{key1},
);

基本上,我想将哈希的键值设置为另一个键值。我已经尝试了上面的语法,但得到以下警告:

Global symbol "%hash" requires explicit package name at ...

我假设这是因为我试图在实际创建之前引用哈希。

6 个答案:

答案 0 :(得分:10)

这是因为,在使用$hash{key1}时,符号“hash”尚未添加到符号表中。你的包还不知道%hash是什么,因为在解析结束“)之前它不会被添加到符号表中 - 但它会更早地遇到符号1行。

您需要提前预先声明%hash:

my %hash;

%hash = (
  "key1" => "value1",
  "key2" => "value2",
  "key3" => $hash{key1},
);

但是,虽然上面将编译,它将无法按预期工作,因为在评估%hash时尚未为$hash{key1}分配任何内容。您需要先将其分配,作为单独的声明:

my %hash;

%hash = (
  "key1" => "value1",
  "key2" => "value2",
);

%hash = %hash, (
  "key3" => $hash{key1},
); # Or simply $hash{key3} = $hash{key1};

请注意,上述仅将值从key1复制到key3,一次。之后他们绝不会联系/关联。如果您想要别名key1key3(例如将它们指向指向相同的值,而不是包含该值的副本),这可能但不会像不重要的。您需要将其设置为绑定哈希并编写自定义绑定处理程序以使其正确,或者使每个键的值为引用

答案 1 :(得分:4)

如果您希望$ hash {key1}成为$ hash {key3}的别名,那么可以使用Data::Alias轻松完成此操作。

use Data::Alias;

my %hash = (
    key1 => 'value1',
    key2 => 'value2'
);

alias $hash{key3} = $hash{key1};

$ hash {key1}和$ hash {key3}现在将引用单个值,并且对另一个的更新将在另一个中可见。

答案 2 :(得分:2)

您可以使用核心模块Hash::Util来访问低级hv_store例程,该例程可以将哈希值合并在一起。 Data::Alias一样整洁,但它已经安装好了。

use Hash::Util 'hv_store';

my %hash = (
  key1 => "value1",
  key2 => "value2",
);

hv_store %hash, key3 => $hash{key1};

say "$_: $hash{$_}" for sort keys %hash;
# key1: value1
# key2: value2
# key3: value1

$hash{key1} = 'new value';

say "$_: $hash{$_}" for sort keys %hash;
# key1: new value
# key2: value2
# key3: new value

如果你不希望别名在初始赋值之后保持不变,那么你可以写下这一行:

$hash{key3} = $hash{key1}
在您的初始声明之后

并跳过使用hv_store

答案 3 :(得分:1)

%hash在创建要分配给哈希值的值列表时不包含任何内容,因为您还没有将列表分配给哈希值。

事实上,%hash在创建要分配给哈希值的值列表时不存在,因为赋值会在其LHS之前评估其RHS。这就是您遇到严格错误的原因。

您可能会喜欢的一些解决方案:

my %hash = (
  key1 => "value1",
  key2 => "value2",
  key3 => "value1",
);


my $value1 = "value1";
my %hash = (
  key1 => $value1,
  key2 => "value2",
  key3 => $value1,
);


my %hash = (
  ( map { $_ => "value1" } qw( key1 key3 ) ),
  key2 => "value2",
);

答案 4 :(得分:1)

为什么不自己动手:

use strict;
use warnings;

sub make_hash (@) { 
    my %h;
    my @unresolved;
    while ( @_ ) {
        my ( $key, $value ) = splice( @_, 0, 2 );
        next unless defined $value;
        if (   not ref( $value ) 
           and my ( $ref ) = $value =~ /^ref:\s*(.*\S)\s*$/ 
           ) {
            if ( defined( my $v = $h{ $ref } )) {
                $h{ $key } = $v;
            }
            else {
                push @unresolved, [ $key, $ref ];
            }
        }
        else {
            $value =~ s/^lit://;
            $h{ $key } = $value;
        }
    }
    $h{ $_->[0] } = $h{ $_->[1] } foreach grep { exists $h{ $_->[0] }; } @unresolved;
    return wantarray ? %h : \%h;
} 

展示一些力量:

my %hash 
    = make_hash(
      'key1' => 'value1'
    , 'key2' => 'value2'
    , 'key3' => 'ref:key1'
    , 'key4' => 'lit:ref:key2'
    , 'key5' => 'lit:lit:ref:key3'
    );

lit:前缀涵盖“如果我真的想要将非引用的值传递为'ref:so-and-so'的情况?它也是递归的回答,“如果我迫切需要使一个值'点亮:xzy'怎么办?

我已经完成了这项工作,并且我也祝福了对Lit类的传递数据或类似内容的引用。

sub Ref ($) { bless \shift, 'Ref' }

然后在make_hash例程中,您只需检查ref( $value ) eq 'Ref'。并指定如下:

my %hash 
    = make_hash(
      'key1' => 'value1'
    , 'key2' => 'value2'
    , 'key3' => Ref 'key1'
    );

有很多方法可以让Perl像你希望的那样行事。

答案 5 :(得分:0)

你可以说

my %h; 
my $i = 0;
while (
    my ( $k, $v ) = ( 
        key1 => '"value1"',
        key2 => '"value2"',
        key3 => '$h{key1}',
    )[ $i, $i + 1 ] 
  )
{
    $h{$k} = eval $v; 
    $i += 2;
}

只要引用的任何键在引用之前出现在列表中。