Определение хеш-значения с использованием другого хеш-значения. - PullRequest
3 голосов
/ 23 января 2012

Есть ли способ сделать следующее, используя только одну структуру данных?

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

Так что, в принципе, я хочу установить значение ключа хеша на другое значение ключа. Я пробовал приведенный выше синтаксис, но получаю следующее предупреждение:

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

Я предполагаю, что это потому, что я пытаюсь сослаться на хеш до того, как он будет создан.

Ответы [ 6 ]

10 голосов
/ 23 января 2012

Это связано с тем, что на момент использования $hash{key1} символ "хэш" еще не был добавлен в таблицу символов.Ваша посылка еще не знает, что такое %hash, потому что она не будет добавлена ​​в таблицу символов до тех пор, пока не будет проанализирован ПОСЛЕ закрытия ")" - однако она встретит строку символа 1 ранее.

Вам необходимо заранее объявить% хеша:

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, один раз .Они никак не связаны / связаны после этого.Если вы хотите использовать псевдонимы key1 и key3 (например, иметь их , указать на одно и то же значение вместо того, чтобы содержать копию значения), это возможно, но не так тривиально.Вам нужно превратить его в связанный хеш и написать собственные обработчики связей, чтобы получить его правильно, или чтобы значение каждого ключа было reference .

4 голосов
/ 23 января 2012

Если вы хотите, чтобы $ 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 голосов
/ 24 января 2012

Вы можете использовать базовый модуль 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.

1 голос
/ 24 января 2012

Почему бы не сделать свою собственную функцию:

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: покрывает случай "Что если я * 1008"* действительно хотел бы передать значение, которое не является ссылкой, как 'ref:so-and-so'? Это также рекурсивно в ответе: «Что, если мне прямо нужно сделать значение« lit: 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 действовать так, как вы хотели бы.

1 голос
/ 24 января 2012

%hash не содержит ничего при создании списка значений для присвоения хешу, поскольку вы еще не присвоили список хешу.

На самом деле, %hash не существует при создании списка значений для присвоения хешу, поскольку присвоения оценивают их RHS до их LHS. Вот почему вы получаете строгую ошибку.

Некоторые решения, которые могут вам понравиться:

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",
);
0 голосов
/ 24 января 2012

Вы могли бы сказать

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;
}

, если все упомянутые ключи появляются в списке, прежде чем на них будут ссылаться.

...