Domanda

Se dovessi avere una semplice classe scalare legata che aumenta ogni volta che viene letta, potrei farlo in questo modo:

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;

Tuttavia per creare una variabile contatore devo usare tie.Potrei creare un contatore ed esportarlo.Ma quello che voglio davvero fare è farlo sembrare OO.Sembra che potrei creare un metodo new come questo:

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

  tie $counter, $class;

  return $counter;
}

quindi nel mio script principale ottieni due contatori facendo:

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

Presumo che questo non funzioni perché un pareggio non sopravvive a una copia (l'ho letto nei documenti da qualche parte), semplicemente non c'è modo di farlo?

NB.So che è solo una questione di stile, ma sembrerebbe più corretto alla vista.

È stato utile?

Soluzione

La magia del legame non viene trasmessa attraverso l'assegnazione perché si applica alla variabile stessa, non al valore che contiene. Hai alcune opzioni:

Restituzione di un riferimento:

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

my $counter = Counter->new;

say $$counter;

Assegnazione a un glob:

our ($counter);

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

say $counter;

Oppure potresti passare la variabile al costruttore:

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

Counter->new(my $counter);

say $counter;

Puoi persino creare un costruttore che funzioni con entrambi i metodi:

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

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

Counter->new(my $lexical);

Negli ultimi due esempi, tie viene passato direttamente a $_[0]. La ragione di ciò è che gli elementi di @_ sono alias per l'elenco degli argomenti, quindi funziona come se avessi digitato il my $counter nella riga tie.


E infine, mentre il tuo esempio è molto chiaro e segue le migliori pratiche, nello spirito di TIMTOWTDI, potresti scrivere l'intera classe in questo modo:

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

Un'ultima cosa da menzionare. Sebbene la tua domanda riguardi le variabili legate, puoi anche utilizzare il sovraccarico per ottenere ciò:

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

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

say $counter;

Ma perdi la possibilità di azzerare il contatore tramite assegnazione. Potresti aggiungere un metodo sub set {$_[0][0] = $_[1]} a Counter.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top