如何在Perl6中编写自定义访问器方法?

Ada*_*uša 16 perl6 raku

如何在Perl6中编写自定义访问器方法?

如果我有这个课程:

class Wizard {
    has Int $.mana is rw;
}
Run Code Online (Sandbox Code Playgroud)

我可以做这个:

my Wizard $gandalf .= new;
$gandalf.mana = 150;
Run Code Online (Sandbox Code Playgroud)

假设我想在我的Perl6类中为一个setter添加一点检查而不放弃$gandalf.mana = 150;表示法(换句话说,我不想写这个:) $gandalf.setMana(150);.如果它试图设定负面法术力,该程序应该死亡.我该怎么做呢?Perl6文档提到它可以编写自定义访问器,但没有说明如何.

Bra*_*ert 18

对于更新版本的Rakudo,有一个名为的子集将UInt其限制为正值.

class Wizard {
  has UInt $.mana is rw;
}
Run Code Online (Sandbox Code Playgroud)

如果你需要这样的东西,你就不会陷入困境; 这是如何定义的:(
你可以放弃my,但我想向你展示Rakudo来源的实际行)

my subset UInt of Int where * >= 0;
Run Code Online (Sandbox Code Playgroud)

你也可以这样做:

class Wizard {
  has Int $.mana is rw where * >= 0;
}
Run Code Online (Sandbox Code Playgroud)

我想指出的是,* >= 0where约束仅仅是创建一个简短的方式赎回.

您可以将以下任何一项作为where约束:

... 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 )
Run Code Online (Sandbox Code Playgroud)

(如果你想要它不是零,你也可以使用... where &prefix:<?>哪个可能更好拼写为... where ?*... where * !== 0)


如果您对使用代码的人感到烦恼,您也可以这样做.

class Wizard {
  has UInt $.mana is rw where Bool.pick; # accepts changes randomly
}
Run Code Online (Sandbox Code Playgroud)

如果你想在总体上查看类中的所有值时确保值"有意义",那么你将需要做更多的工作.
(可能还需要更多的实施知识)

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
      }
    )
  }
}
Run Code Online (Sandbox Code Playgroud)


dar*_*rch 13

您可以$.mana通过声明方法获得与提供相同的访问者界面is rw.然后你可以围绕底层属性包装代理,如下所示:

#!/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';
Run Code Online (Sandbox Code Playgroud)

Proxy基本上是拦截对存储的读写调用并执行除默认行为之外的其他操作的方法.由于他们的资本建议,FETCH以及STORE由Perl的自动调用来解决般的表情$gandalf.mana = $gandalf.mana + 5.

有一个更全面的讨论,包括你是否应该在PerlMonks尝试这个.我会建议反对上述 - 以及公共rw属性.它更多地展示了用语言表达的内容而不是有用的工具.