Perl: определить хеш со ссылкой на тот же хеш, $ this -> {key}? - PullRequest
5 голосов
/ 27 января 2010

Как бы я создал хеш, как показано ниже:

my %hash = (key1=>"Something", key2=>$hash{key1} . "Else");

Разве это нельзя сделать, когда я объявляю хеш? Пока единственное, что я придумал, было:

my %hash = (key1=>"Something");
$hash{key2} = $hash{key1} . "Else";

Ответы [ 7 ]

11 голосов
/ 27 января 2010

Почему бы не использовать промежуточную переменную?

my $common_value = 'Something';
my %some_hash = ( k1 => $common_value, k2 => $common_value.'Else' );

Обновление

Кемп спросил, зачем нам нужна промежуточная переменная. Я могу интерпретировать этот вопрос двумя способами, я постараюсь ответить на обе интерпретации.

  1. Почему вы не можете сделать my %hash = ( k1 => 'foo', k2 => $hash{k1}.'bar' );?

    Правая часть (rhs) выражения оценивается перед левой частью (lhs). Таким образом, когда значение для k2 определено, присвоение %hash еще не произошло. Фактически, %hash еще даже не был создан и введен в блокнот. (%hash здесь - лексическая переменная, поэтому она не входит в таблицу символов.)

  2. Зачем использовать переменную, а не другой метод?

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

    my $cv1 = sub_result() * 5;
    my $cv2 = "Number: $cv1";
    
    my %h = (
        k1 => $cv1,  
        k2 => $cv2,
        k3 => "Numero: $cv1",
        k4 => "Big $cv2",
        k5 => "Round $cv2",
        k6 => "Whole $cv2",
        k7 => "-$cv1",
    );
    

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

    use Scalar::Util qw(reftype);
    
    my @init = ( 
        [ key1 => $value                 ],
        [ key2 => \&make_a_value, 'key1' ],
        [ key3 => \&another_way,  'key2' ],
    );
    
    my %h; 
    for my $spec ( @init ) {
        my $key   = shift @$spec;
        my $value = shift @$spec;
        my @args  = @$spec;
    
        if( reftype $value eq reftype sub {} ) {
            $value = $value->( @h{ @args } );
        }
    
        $h{$key} = $value;
    }
    

    В любом случае, когда возникает этот вопрос, выбор того, какую технику использовать, зависит от многих деталей, характерных для этого экземпляра. Таким образом, мы остаемся с необходимостью делать общие заявления. Промежуточные переменные являются одним из хороших способов решения проблемы ОП. Должны ли они использоваться в его конкретном случае, я не могу сказать.

9 голосов
/ 27 января 2010

Это невозможно сделать по 2 причинам:

  1. В то время, когда Perl обрабатывает key2 => $hash{key1}, он еще не связал память, выделенную ключом 1, с хэш-именем имени хеша. Другими словами, $hash{key1} не указывает на правильное место в памяти, пока не назначено key2 => $hash{key1}.

  2. Предыдущая причина не указала дополнительную проблему: вы еще не поместили %hash в таблицу символов к тому времени, когда вы обрабатываете key2 => $hash{key1} (снова порядок синтаксического анализа / оценки), поэтому компилятор Будет ли Barf, если вы use strict;, как вы всегда должны. Это легко исправить, объявив my %hash в предыдущем заявлении, но # 1 - нет.

4 голосов
/ 27 января 2010

В точке, где вы определяете хеш, $ hash {key1} не существует, поэтому вы не можете сделать это за один оператор. Вы можете сделать свое собственное решение и использовать ключ key1 после того, как вы его определили.

1 голос
/ 27 января 2010

Что не так с

my %hash = ( key1 => "Something", key2 => "Something" . "Else" );

Да, я знаю, что это заполнители, но кем бы они ни были, вы знаете их во время создания хеша (или вообще не будете его создавать), так почему бы вам просто не объединить их вместе? 1006 *

0 голосов
/ 28 января 2010

Если создание промежуточной переменной за пределами % хеша отключено, то использование do block может сделать ее более эстетически привлекательной:

my %hash = (
    do {
        my $s = 'Something';
        ( key1 => $s, key2 => $s . 'Else' );
    },
);

/ I3az /

0 голосов
/ 27 января 2010

Вы можете подойти довольно близко, но я не уверен, что это имеет значение, кроме новизны:

$_->{key2} = $_->{key1} . "Else" for my $hash = { key1 => "Something" };
0 голосов
/ 27 января 2010

Во-первых, я бы просто сделал это с промежуточным значением. Но у вас есть два отдельных более продвинутых варианта. Я не предлагаю / любой / из них, если вы новичок в Perl - на самом деле мне больше нравится решение с промежуточным значением, но для полноты:

  • Вы можете использовать Tie :: Hash , который позволяет сделать объект доступным с помощью синтаксиса хеширования и разыменования хеш-стиля (->{key1}), более подробную информацию об этом методе можно получить с помощью perldoc perltie и указанная выше страница справочника для Tie::Hash;
  • Вы можете отказаться от идеи использования хэша; и используйте объект или даже Moose , который позволит вам инициализировать объект из хеша, используя то же значение. Я не вижу особой цели в этом примере.

Вот пример Moose.

package Class;
use Moose;
has [qw/key1 key2/] => ( isa => 'Str', is => 'rw', init_arg => 'key1' );

my $o = Class->new( { key1 => 'foobar' } );
print $o->key1, $o->key2;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...