Constructor para escalar vinculado
-
29-10-2019 - |
Pregunta
Si tuviera una clase escalar ligada simple que se incrementa cada vez que se lee, podría hacerlo así:
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;
Sin embargo, para crear una variable de contador tengo que usar tie
.Podría crear un contador y exportarlo.Pero lo que realmente quiero hacer es que parezca OO.Parece que podría crear un método new
como este:
sub new {
my $class = shift;
my $counter;
tie $counter, $class;
return $counter;
}
luego, en mi secuencia de comandos principal, obtengo dos contadores haciendo:
my $counter1 = Counter->new();
my $counter2 = Counter->new();
Supongo que esto no funciona porque un empate no sobrevive a una copia (lo leí en los documentos en alguna parte), ¿simplemente no hay forma de hacer esto?
NB.Sé que es solo una cuestión de estilo, pero se verá más correcto a la vista.
Solución
La magia de empate no se transfiere a la asignación porque se aplica a la variable en sí, no al valor que contiene. Tiene algunas opciones:
Devolver una referencia:
sub new {tie my $ret, ...; \$ret}
my $counter = Counter->new;
say $$counter;
Asignación a un globo:
our ($counter);
*counter = Counter->new; # same new as above
say $counter;
O puede pasar la variable al constructor:
sub new {my $class = shift; tie $_[0], $class}
Counter->new(my $counter);
say $counter;
Incluso puedes crear un constructor que funcione con ambos métodos:
sub new {
my $class = shift;
tie $_[0] => $class;
\$_[0]
}
our $glob; *glob = Counter->new;
Counter->new(my $lexical);
En los dos últimos ejemplos, tie
se pasa $_[0]
directamente. La razón de esto es que los elementos de @_
son alias de la lista de argumentos, por lo que funciona como si hubiera escrito my $counter
en la línea tie
.
Y finalmente, si bien su ejemplo es muy claro y sigue las mejores prácticas, en el espíritu de TIMTOWTDI, podría escribir toda su clase así:
{package Counter;
sub TIESCALAR {bless [0]}
sub FETCH {$_[0][0]++}
sub STORE {$_[0][0] = $_[1]}
sub new {tie $_[1] => $_[0]; \$_[1]}
}
Una última cosa que mencionar. Si bien su pregunta es sobre variables vinculadas, también puede usar la sobrecarga para lograr esto:
{package Counter;
use overload fallback => 1, '""' => sub {$_[0][0]++};
sub new {bless [0]}
}
my $counter = Counter->new; # overloading survives the assignment
say $counter;
Pero pierde la capacidad de restablecer el contador mediante una asignación. Puede agregar un método sub set {$_[0][0] = $_[1]}
a Counter
.