Construtor para escalar vinculado
-
29-10-2019 - |
Pergunta
Se eu tivesse uma classe escalar vinculada simples que aumentasse toda vez que fosse lida, eu poderia fazer assim:
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;
Porém para criar uma variável de contador eu tenho que usar tie
.Eu poderia criar um contador e exportá-lo.Mas o que eu realmente quero fazer é fazer com que pareça OO.Parece que eu poderia criar um new
método como este:
sub new {
my $class = shift;
my $counter;
tie $counter, $class;
return $counter;
}
então, no meu script principal, obtenha dois contadores fazendo:
my $counter1 = Counter->new();
my $counter2 = Counter->new();
Presumo que isso não funcione porque um empate não sobrevive a uma cópia (li isso nos documentos em algum lugar). Simplesmente não há maneira de fazer isso?
Nota.Eu sei que é apenas uma questão de estilo, mas pareceria mais correto aos olhos.
Solução
A magia do empate não é realizada na atribuição porque se aplica à própria variável, não ao valor que ela contém.Você tem poucas opções:
Retornando uma referência:
sub new {tie my $ret, ...; \$ret}
my $counter = Counter->new;
say $$counter;
Atribuindo a um globo:
our ($counter);
*counter = Counter->new; # same new as above
say $counter;
Ou você pode passar a variável para o construtor:
sub new {my $class = shift; tie $_[0], $class}
Counter->new(my $counter);
say $counter;
Você pode até criar um construtor que funcione com os dois métodos:
sub new {
my $class = shift;
tie $_[0] => $class;
\$_[0]
}
our $glob; *glob = Counter->new;
Counter->new(my $lexical);
Nos dois últimos exemplos, tie
é passado $_[0]
diretamente.A razão para isso é que os elementos @_
são aliases para a lista de argumentos, então funciona como se você tivesse digitado o my $counter
no tie
linha.
E, finalmente, embora seu exemplo seja muito claro e siga as melhores práticas, no espírito do TIMTOWTDI, você poderia escrever toda a sua aula assim:
{package Counter;
sub TIESCALAR {bless [0]}
sub FETCH {$_[0][0]++}
sub STORE {$_[0][0] = $_[1]}
sub new {tie $_[1] => $_[0]; \$_[1]}
}
Uma última coisa a mencionar.Embora sua pergunta seja sobre variáveis vinculadas, você também pode usar a sobrecarga para conseguir isso:
{package Counter;
use overload fallback => 1, '""' => sub {$_[0][0]++};
sub new {bless [0]}
}
my $counter = Counter->new; # overloading survives the assignment
say $counter;
Mas você perde a capacidade de zerar o contador por meio de atribuição.Você poderia adicionar um sub set {$_[0][0] = $_[1]}
método para Counter
.