Обратный вызов для доступа к свойству хеша в 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