Frage Wie schreibt man benutzerdefinierte Accessor-Methoden in Perl6?


Wie schreibt man benutzerdefinierte Accessor-Methoden in Perl6?

Wenn ich diese Klasse habe:

class Wizard {
    has Int $.mana is rw;
}

Ich kann dies tun:

my Wizard $gandalf .= new;
$gandalf.mana = 150;

Nehmen wir an, ich möchte einen Setter in meiner Perl6-Klasse ein wenig hinzufügen, ohne die $gandalf.mana = 150; Notation (mit anderen Worten, ich möchte das nicht schreiben: $gandalf.setMana(150);). Das Programm sollte sterben, wenn es versucht, ein negatives Mana zu setzen. Wie mache ich das? Die Perl6-Dokumentation erwähnt nur, dass es möglich ist, benutzerdefinierte Accessoren zu schreiben, sagt aber nicht wie.


12
2017-07-28 00:05


Ursprung


Antworten:


Sie können die gleiche Accessor-Schnittstelle wie diese erhalten $.mana bietet durch Deklarieren einer Methode is rw. Dann können Sie einen Proxy wie folgt um das zugrundeliegende Attribut wickeln:

#!/usr/bin/env perl6
use v6;

use Test;
plan 2;

class Wizard {
    has Int $!mana;

    method mana() is rw {
        return Proxy.new:
            FETCH => sub ($) { return $!mana },
            STORE => sub ($, $mana) {
                die "It's over 9000!" if ($mana // 0) > 9000;
                $!mana = $mana;
            }
    }
}

my Wizard $gandalf .= new;
$gandalf.mana = 150;
ok $gandalf.mana == 150, 'Updating mana works';
throws_like sub {
    $gandalf.mana = 9001;
}, X::AdHoc, 'Too much mana is too much';

Proxy ist im Grunde eine Möglichkeit, Lese- und Schreibaufrufe an Speicher abzufangen und etwas anderes als das Standardverhalten zu tun. Wie ihre Kapitalisierung nahelegt, FETCH und STORE werden von Perl automatisch aufgerufen, um Ausdrücke wie z $gandalf.mana = $gandalf.mana + 5.

Es gibt eine ausführlichere Diskussion darüber, ob Sie das überhaupt versuchen sollten PerlMonks. Ich würde empfehlen, gegen die oben genannten - und die Öffentlichkeit rw Attribute im Allgemeinen. Es ist mehr eine Darstellung dessen, was man in der Sprache ausdrücken kann als ein nützliches Werkzeug.


11
2017-07-28 18:16



Mit neueren Versionen von Rakudo gibt es eine Untergruppe namens UInt das beschränkt es auf positive Werte.

class Wizard {
  has UInt $.mana is rw;
}

Damit Sie nicht im Stich stecken, wenn Sie so etwas brauchen; Hier ist wie das definiert ist:
(Sie können das verlassen my, aber ich wollte dir das zeigen aktuelle Zeile von der Rakudo Quelle)

my subset UInt of Int where * >= 0;

Sie könnten dies auch tun:

class Wizard {
  has Int $.mana is rw where * >= 0;
}

Ich möchte darauf hinweisen, dass die * >= 0 in dem where Constraint ist nur ein kurzer Weg, um ein Abrufbar.

Du könntest folgendes haben als: where Zwang:

... where &subroutine # a subroutine that returns a true value for positive values
... where { $_ >= 0 }
... where -> $a { $a >= 0 }
... where { $^a >= 0 }
... where $_ >= 0 # statements also work ( 「$_」 is set to the value it's testing )

(Wenn Sie wollten, dass es nicht Null ist, könnten Sie auch verwenden ... where &prefix:<?> das ist wahrscheinlich besser als geschrieben ... where ?* oder ... where * !== 0 )


Wenn Sie möchten, dass Leute, die Ihren Code verwenden, nervig sind, können Sie dies auch tun.

class Wizard {
  has UInt $.mana is rw where Bool.pick; # accepts changes randomly
}

Wenn Sie sicherstellen möchten, dass der Wert "sinnvoll" ist, wenn Sie alle Werte in der Klasse zusammen betrachten, müssen Sie viel mehr arbeiten.
(Es kann viel mehr Wissen über die Implementierung erfordern)

class Wizard {
  has Int $.mana; # use . instead of ! for better `.perl` representation

  # overwrite the method the attribute declaration added
  method mana () is rw {
    Proxy.new(
      FETCH => -> $ { $!mana },
      STORE => -> $, Int $new {
        die 'invalid mana' unless $new >= 0; # placeholder for a better error
        $!mana = $new
      }
    )
  }
}

14
2017-07-28 18:13