Обратный вызов для доступа к свойству хеша в perl

Есть ли в Perl какой-то нативный способ узнать, к какому ключу хэша обращались?
Что-то вроде магических методов, существующих в некоторых языках, или прокси-объект?

2 ответа

Решение

Да, есть. Это называется "связывание" переменной.

tie является комбинацией создания экземпляра прокси-объекта (из указанного класса) и связывания его с переменной.

Увидеть perldoc perltie для деталей.

Краткая версия:

tie %hash, 'Some::Class';

И затем доступ %hash вызовет вызовы метода в Some::Class (при условии, что он реализует TIEHASH конструктор и остальная часть интерфейса).

Perl позволяет вам tie переменная к классу. Это обеспечивает механизм для вставки вашего пользовательского кода (кода вашего связанного класса) в различные операции, которые могут быть задействованы в связанной переменной. Для хэша эти операции TIEHASH, STORE, FIRSTKEY, FETCH, NEXTKEY, EXISTS, DELETE, CLEAR, а также SCALAR,

Поэтому полная реализация связанного хэша будет реализовывать каждый из этих методов. Чтобы сделать это проще, основной дистрибутив Perl предоставляет модуль Tie::Hash, который также обеспечивает Tie::StdHash,

Tie::StdHash Вероятно, это кратчайший путь для добавления некоторой функциональности в довольно типичную хеш-реализацию, так как он обеспечивает стандартные реализации каждого из хеш-методов, упомянутых выше. Чтобы добавить свою собственную функциональность, вы можете просто переопределить те методы, для которых имеет смысл это сделать. Тогда либо взывать к SUPER::* (где * представляет имя переопределенной операции) или предоставьте желаемую функциональность хэша полностью в переопределенном методе. Пример, вероятно, проще, чем слова:

package NoisyHash;

use strict;
use warnings;

require Tie::Hash;
our @ISA = q(Tie::StdHash);

sub STORE {
    my ($self, $key, $value) = @_;
    warn "\tSet $key to $value\n";
    return $self->SUPER::STORE($key, $value);
}

sub FETCH {
    my ($self, $key) = @_;
    warn "\tFetch from $key\n";
    return $self->SUPER::FETCH($key);
}

1;


package main;

use strict;
use warnings;

tie my %hash, 'NoisyHash';

$hash{A} = 1;
$hash{B} = 'foo';

print "\$hash{A} = $hash{A}\n";
print "\$hash{B} = $hash{B}\n";

В этом примере FETCH а также STORE методы переопределяются с кодом, который заставляет их выплевывать некоторые диагностические STDERR, И затем мы сохраняем регулярную хеш-семантику для этих методов, наконец вызывая SUPER::*, Мы могли бы просто реализовать наши собственные версии этой функциональности, а не вызывать SUPER, но использование существующей реализации менее подвержено ошибкам.

Выход из предыдущего примера:

    Set A to 1
    Set B to foo
    Fetch from A
$hash{A} = 1
    Fetch from B
$hash{B} = foo

Как показано в примере, tie функция используется для привязки NoisyHash в %hash, Оказывается, что tie также возвращает объект, который не часто используется, но может использоваться для предоставления дополнительных методов, которые могут быть вызваны для хеша, которые не являются частью набора хеш-операций по умолчанию, перечисленных выше.

package SumHash;

use strict;
use warnings;

require Tie::Hash;
our @ISA = q(Tie::StdHash);
use List::Util;

sub sum {
    my $self = shift;
    return List::Util::sum(values %$self);
}

1;

package main;

use strict;
use warnings;

my $hash_obj = tie my %hash, 'SumHash';

@hash{qw(a b c d e)} = (1, 2, 3, 4, 5);

my $sum = $hash_obj->sum;
print "Sum of (",
      join(', ', values %hash),
      ") is $sum.\n",

Пример вывода из этого кода:

Sum of (3, 4, 1, 5, 2) is 15.

Итак, здесь мы называем sum метод, который мы поместили в SumHash учебный класс. Класс все еще наследует от Tie::StdHash и не переопределяет никакие стандартные методы, поэтому он все еще сохраняет стандартную функциональность хеша. Но это добавляет возможность подсчитывать значения хеша. Однако это не самый простой способ подсчитать значения хеша. Вместо того, чтобы пытаться связать хеш, чтобы добавить суммирование, можно просто сделать это:

use List::Util qw(sum);
print "Sum of (", join(', ', values %hash), ") is ", sum(values %hash), "\n";

Это иллюстрирует, почему в сообществе Perl существует общее мнение, что связывание переменных редко является лучшим подходом к решению типичных проблем. Связывание переменных создает трудности для подражания и рассуждения о действиях на расстоянии. И связанные переменные имеют значительно худшую производительность. Но эта практика иногда имеет оправданное применение, и Perl предоставляет средства.

Увидеть perldoc Tie::Hash для объяснения этого модуля. И помните, что tie может использоваться для связывания любого из общих контейнеров Perl: скаляра, массива, хэша или дескриптора файла.

Наиболее полное объяснение в документации Perl для использования tie доступен на perldoc perltie

Другие вопросы по тегам