Как мне взаимодействовать с объектом Perl, который имеет атрибут hash? - PullRequest
4 голосов
/ 26 мая 2010

У меня есть класс с несколькими переменными, одна из которых является хешем (_runs):

sub new
{
    my ($class, $name) = @_;
    my $self = {
        _name => $name,
        ...
        _runs => (),
        _times => [],
        ...
    };
    bless ($self, $class);
    return $self;
}

Теперь все, что я пытаюсь сделать, это создать аксессор / мутатор, а также другую подпрограмму, которая помещает новые данные в хеш. Но у меня адское время, когда все референсные / разыменовывающие / $ self звонки работают вместе. Я почти сжег свои глаза из-за ошибок «Не могу использовать строку (« бла ») в качестве хэш-ссылки и т. Д.

Что для метода доступа, что является «наилучшей практикой» для возврата хешей? Какой из этих вариантов я должен использовать (если есть)?

return $self->{_runs};
return %{ $self->{_runs} };
return \$self->{_runs};

Кроме того, когда я использую хеш в других подпрограммах класса, какой синтаксис я использую для его копирования?

my @runs = $self->{_runs};
my @runs = %{ $self->{_runs} };
my @runs = $%{ $self->{_runs} };
my @runs = $$self->{_runs};

То же самое касается перебора ключей:

foreach my $dt (keys $self->{_runs})
foreach my $dt (keys %{ $self->{_runs} })

А как насчет фактического добавления данных?

$self->{_runs}{$dt} = $duration;
%{ $self->{_runs} }{$dt} = $duration;
$$self->{_runs}{$dt} = $duration;

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

Ответы [ 3 ]

6 голосов
/ 26 мая 2010

Вы храните ссылки на массив или хэши в вашем объекте. Чтобы использовать их со стандартными функциями, вам нужно разыменовать их. Например:

@{ $self->{_array_ref_key} }; 
%{ $self->{_hash_ref_key} };

Если вам нужно передать параметры стандартной функции:

push( @{ $self->{_array_ref_key} }, $some_value );
for my $hash_key ( keys %{ $self->{_hash_ref_key} }) {
    $self->{_hash_ref_key}{$hash_key}; ## you can access hash value by reference
}

Также синтаксис $self->{_hash_ref_key}{$hash_key} является сокращением для $self->{_hash_ref_key}->{$hash_key} (что может иметь смысл, если вы видите его впервые).

Также взгляните на соответствующую страницу руководства .

5 голосов
/ 26 мая 2010

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

use warnings;
my $self = {
    _name => $name,
    _runs => (),
    _times => [],
};
bless ($self, $class);

use Data::Dump::Streamer; DumpLex $self;

__END__
Odd number of elements in anonymous hash at …

$self = bless( {
    _name             => undef,
    _runs             => '_times',
    "ARRAY(0x88dcb8)" => undef,
}, '…' );

Все элементы в списке образуют пары ключ / значение для хэша, ссылка на который будет bless ed. () - пустой список, так что вы действительно выражаете список '_name', $name, '_runs', '_times', []. Вы можете видеть, что _times перемещается вверх, чтобы стать значением, а ссылка [] переводится в строку как хэш-ключ. Вы получаете предупреждение, потому что для него не осталось значения; это будет автоматически приведено к undef. (Всегда всегда включайте warnings прагму.)

Теперь для части с кишками: хеш-значения должны быть скалярными. Массивы и хэши не являются; но ссылки на них есть. Таким образом:

my $self = {
    _name => $name,
    _runs => {},
    _times => [],
};
3 голосов
/ 27 мая 2010

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

Если вы хотите вернуть копию данных или какие-либо изменения в возвращаемых данных не влияют на копию в объекте, вы не можете сделать простые решения, о которых вам говорят другие ответы, потому что они возвращают мелкие копии , которые все еще будут делиться внутренними ссылками. Вам нужно сделать глубокую копию , а затем вернуть отключенную структуру данных. Storable делает это легко с dclone:

 use Storable qw( dclone );

 sub some_method {
      my( $self, ... ) = @_;
      ...;
      my $clone = dclone( $self->{_runs} );
      $clone;
      }

Если вы хотите, чтобы более высокий уровень изменил объект путем изменения возвращенной структуры данных, просто верните ссылку, которую вы уже сохранили. Вам не нужно ничего делать для этого:

 sub some_method {
      my( $self, ... ) = @_;
      ...;
      $self->{_runs};
      }

Кроме того, ваша задача - создать интерфейс, чтобы людям не приходилось думать о вашей структуре данных на более высоком уровне. Вы инкапсулируете все, чтобы детали реализации не показывались. Таким образом, вы можете изменить реализацию, не нарушая код более высокого уровня (если интерфейс стабилен).

Вы создаете метод runs, который возвращает список прогонов:

 sub get_run_keys {
      my( $self ) = @_;

      keys %{ $self->{_runs} };
      }

Или, может быть, вы просто хотите значения:

 sub get_run_values {
      my( $self ) = @_;

      values %{ $self->{_runs} };
      }

Или, может быть, все это:

 sub get_run_hash {
      my( $self ) = @_;

      $self->{_runs}; # subject to the cloning stuff I mentioned earlier
      }

Когда вы хотите получить значения для определенного прогона, вы получаете к нему доступ другим способом:

 sub get_run {
      my( $self, $key ) = @_;

      $self->{_runs}{$key};
      }

Установка значения прогона аналогична:

 sub set_run {
      my( $self, $key, $value ) = @_;

      $self->{_runs}{$key} = $value;
      }

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

 foreach my $key ( $self->get_run_keys ) {
     my $run = $self->get_run( $key );
     ...;
     $self->set_run( $key, $new_value );
     }

Объектно-ориентированный дизайн - большая тема, и вы можете многое сделать. Этого достаточно, чтобы начать. Вы также можете перенести другие операции:

 sub does_run_exist {
      my( $self, $key ) = @_;

      exists $self->{_runs}{$key};
      }

 sub delete_runs {
      my( $self, @keys ) = @_;

      delete $self->{_runs}{$key} foreach my $keys ( @keys );
      }

 sub reset_runs {
      my( $self, $key ) = @_;

      $self->{_runs} = {};
      }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...