Конструктор для связанного скаляра - PullRequest
2 голосов
/ 05 февраля 2011

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

package Counter;

use strict;
use warnings;

sub TIESCALAR {
  my $class = shift;
  my $value = 0;

  bless \$value, $class;

  return \$value;
}

sub FETCH {
  my $self = shift;

  my $value = $$self;

  $$self++;

  return $value;
}

sub STORE {
  my $self = shift;
  $$self = shift;
}

1;

Однако, чтобы создать переменную счетчика, я должен использовать tie. Я мог бы создать один счетчик и экспортировать его. Но я действительно хочу, чтобы это выглядело ОО. Кажется, я мог бы создать new метод, подобный этому:

sub new {
  my $class = shift;
  my $counter;

  tie $counter, $class;

  return $counter;
}

тогда в моем основном скрипте получите два счетчика, выполнив:

my $counter1 = Counter->new();
my $counter2 = Counter->new();

Я предполагаю, что это не работает, потому что связка не переживает копию (я читал это где-то в документации), просто нет способа сделать это?

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

1 Ответ

11 голосов
/ 05 февраля 2011

Магия связи не переносится через присвоение, потому что она применяется к самой переменной, а не к значению, которое она содержит.У вас есть несколько вариантов:

Возвращение ссылки:

sub new {tie my $ret, ...; \$ret}

my $counter = Counter->new;

say $$counter;

Назначение глобу:

our ($counter);

*counter = Counter->new; # same new as above

say $counter;

Или вы можете передать переменную в конструктор:

sub new {my $class = shift; tie $_[0], $class}

Counter->new(my $counter);

say $counter;

Вы даже можете создать конструктор, который работает с обоими методами:

sub new {
    my $class = shift;
    tie $_[0] => $class;
    \$_[0]
}

our $glob; *glob = Counter->new;

Counter->new(my $lexical);

В последних двух примерах tie передается $_[0] напрямую.Причина этого в том, что элементы @_ являются псевдонимами в списке аргументов, поэтому он работает так, как если бы вы набрали my $counter в строке tie.


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

{package Counter;
    sub TIESCALAR {bless [0]}
    sub FETCH {$_[0][0]++}
    sub STORE {$_[0][0] = $_[1]}
    sub new {tie $_[1] => $_[0]; \$_[1]}
}

И последнее, что нужно упомянуть.Хотя ваш вопрос касается связанных переменных, вы также можете использовать перегрузку для достижения этого:

{package Counter;
    use overload fallback => 1, '""' => sub {$_[0][0]++};
    sub new {bless [0]}
}

my $counter = Counter->new;  # overloading survives the assignment

say $counter;

Но вы теряете возможность сбрасывать счетчик с помощью присваивания.Вы можете добавить sub set {$_[0][0] = $_[1]} метод к Counter.

...