Question

Si je devais avoir une classe scalaire liée simple qui incrémente chaque fois qu'il est lu, je pourrais faire ça comme ceci:

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;

Cependant, pour créer une variable de comptoir, je dois utiliser tie. Je pourrais créer un compteur et l'exporter. Mais ce que je veux vraiment faire, c'est le faire paraître. Il semble que je pourrais créer un new méthode comme ceci:

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

  tie $counter, $class;

  return $counter;
}

Ensuite, dans mon script principal, obtenez deux compteurs en faisant:

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

Je suppose que cela ne fonctionne pas parce qu'une égalité ne survit pas à une copie (je lis cela dans les documents quelque part), n'y a-t-il tout simplement aucun moyen de le faire?

NB. Je sais que ce n'est qu'une question de style, mais cela aurait l'air plus correct à l'œil.

Était-ce utile?

La solution

Tie Magic n'est pas transportée sur la mission car elle s'applique à la variable elle-même, pas à la valeur qu'elle contient. Vous avez quelques options:

Renvoi d'une référence:

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

my $counter = Counter->new;

say $$counter;

Attribution à un globe:

our ($counter);

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

say $counter;

Ou vous pouvez passer la variable dans le constructeur:

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

Counter->new(my $counter);

say $counter;

Vous pouvez même créer un constructeur qui fonctionne avec les deux méthodes:

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

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

Counter->new(my $lexical);

Dans les deux derniers exemples, tie est passé $_[0] directement. La raison en est que les éléments de @_ sont des alias sur la liste des arguments, donc cela fonctionne comme si vous aviez tapé le my $counter dans le tie ligne.


Et enfin, bien que votre exemple soit très clair et suit les meilleures pratiques, dans l'esprit de Timtowtdi, vous pouvez écrire toute votre classe comme ceci:

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

Une dernière chose à mentionner. Bien que votre question concerne les variables liées, vous pouvez également utiliser la surcharge pour y parvenir:

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

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

say $counter;

Mais vous perdez la possibilité de réinitialiser le compteur via l'affectation. Vous pouvez ajouter un sub set {$_[0][0] = $_[1]} méthode pour Counter.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top