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.

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top