Costruttore per scalare legato
-
29-10-2019 - |
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.
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
.