【问题标题】:Can the raku built in accessor methods be overridden by a child class?raku 内置访问器方法可以被子类覆盖吗?
【发布时间】:2020-09-20 12:24:39
【问题描述】:

我正在尝试“覆盖” raku 为公共 $ 生成的内置访问器。属性。但是,我可以使用一些帮助来尝试找出此代码失败的原因...

class Measure {
    has $.value is rw
}

class Angle is Measure {
    multi method value( $x ) { 
        nextwith( $x % 360 );
    }   
}

my $m = Measure.new( value => 27 );
$m.value = 42;     
say $m;   #Measure.new(value => 42)

my $a = Angle.new( value => 27 );
$a.value = 43; 
say $a;   #Error...

Cannot resolve caller value(Angle:D: ); none of these signatures match:
    (Angle: $x, *%_)

您的指导会很有帮助!

【问题讨论】:

  • 这个问题让parts of an answer I wrote to another of your questions重新受到关注。阅读整个答案,或专注于“虽然上述内容不太正确”的部分。和脚注 2。Anglevalue 方法正在遮蔽 Measure 但签名错误;它有一个参数($x),但访问器方法必须有零个参数。把它们放在一起,你就会得到我们在这里看到的行为。
  • 嗨@raiph - 谢谢你的反馈 - 我想这很不直观(对我来说)$。访问者不接受争论,所以我注定要绕同一个小圈子(好吧,也许这次我会学习)

标签: oop raku


【解决方案1】:

当您使用. 将属性声明为公共属性时,Raku 将创建一个具有相同名称的方法。

该方法不接受任何参数。
就算是rw.
(如果将属性声明为is rw,那么生成的方法就是实际标记为is rw。)

当您使用nextwith 时,它会分派给父类中的方法。

在这种情况下,该方法不接受任何参数。


这至少是可行的:

class Angle is Measure {
    method value( $x ) { 
        callwith() = $x % 360;
    }   
}

my $a = Angle.new( value => 27 );
#$a.value = 43; 
$a.value(43);

当然这意味着Angle.value 不像Measure.value 那样是一个左值
左值表示可以在=的左边。)

那么让我们这样做吧。

由于我们需要在调用.value 的过程中进行计算,所以我们需要返回一个Proxy

class Angle is Measure {
    method value() is rw { 
        Proxy.new:
            FETCH => -> $ { self.Measure::value },
            STORE => -> $, $x {
                self.Measure::value = $x % 360;
            }
    }   
}

请注意,我们不能只在这些块中使用 callsame 或类似名称,因为它们会启动新的调度链。
相反,我们需要调用 Measure 类中的方法版本。

如果您将该调用的结果绑定到您用作闭包一部分的变量,则可以使用callsame 或类似名称。
(我使用了$attr,因为它绑定到实际的属性标量。)

class Angle is Measure {
    method value is rw {
        my $attr := callsame();
        Proxy.new:
            FETCH => -> $ { $attr },
            STORE => -> $, $x {
                $attr = $x % 360;
            }
    }
}

我个人认为Measure 可能应该是一个角色,因为这使事情变得更容易,因为您可以直接访问该属性。

role Measure {
    has $.value is rw
}

class Angle does Measure {
    method value() {
        Proxy.new:
            FETCH => -> $ { $!value },
            STORE => -> $, $x {
                $!value = $x % 360;
            }
    }   
}

我也有一个问题,即角度被声明为只是一个数字,而没有说它是度数而不是弧度或弧度。

实际上你甚至没有将它声明为一个数字。

所以我可能会尝试这样的事情:

role Measure {
    has Real $.value is rw;
}

role Angle {…}

class Degrees  {…}
class Radians  {…}
class Gradians {…}

role Angle does Measure {
    method Degrees  ( --> Degrees  ) {…}
    method Radians  ( --> Radians  ) {…}
    method Gradians ( --> Gradians ) {…}
}

class Degrees does Angle {
    method value() {
        Proxy.new:
            FETCH => -> $ { $!value },
            STORE => -> $, $x {
                $!value = $x % 360;
            }
    }
    method Degrees  () { self }
    method Radians  () { !!! } # needs to actually be implemented here
    method Gradians () { !!! }
}
class Radians does Angle {
    method value() {
        Proxy.new:
            FETCH => -> $ { $!value },
            STORE => -> $, $x {
                $!value = $x % τ;
            }
    }
    method Degrees  () { !!! }
    method Radians  () { self }
    method Gradians () { !!! }
}
class Gradians does Angle {
    method value() {
        Proxy.new:
            FETCH => -> $ { $!value },
            STORE => -> $, $x {
                $!value = $x % 400;
            }
    }
    method Degrees  () { !!! }
    method Radians  () { !!! }
    method Gradians () { self }
}

老实说,我也不喜欢这样,因为您将值视为容器。

基本上你让它像这样工作,你不能有一个恒定的角度。

 class Foo {
     has Angle $.bar;
 }

 my $foo = Foo.new( bar => Degrees.new( value => 27 ) );
 $foo.bar.angle = 43;

我认为你应该要求它像这样工作,你可以选择角度是否恒定。

 class Foo {
     has Angle $.bar is rw;
 }

 my $foo = Foo.new( bar => Degrees.new( value => 27 ) );
 $foo.bar .= new( value => 43 );

这样做可以让您直接删除所有子类中的value 方法,并用简单的TWEAK 替换它们。 (无论如何,这是您真正需要的东西。)
当然,您还需要从$.value 中删除is rw

我会这样做,以便您可以仅使用单个值而不是 value => 27 调用 .new

role Measure {
    has Real $.value;

    multi method new ( $value ) {
        samewith( :$value )
    }
}

role Angle {…}

class Degrees  {…}
class Radians  {…}
class Gradians {…}

role Angle does Measure {
    method Degrees  ( --> Degrees  ) {…}
    method Radians  ( --> Radians  ) {…}
    method Gradians ( --> Gradians ) {…}
}

class Degrees does Angle {
    submethod TWEAK { $!value %= 360 }
    method Degrees  () { self }
    method Radians  () { !!! } # needs to actually be implemented here
    method Gradians () { !!! }
}
class Radians does Angle {
    submethod TWEAK { $!value %= τ }
    method Degrees  () { !!! }
    method Radians  () { self }
    method Gradians () { !!! }
}
class Gradians does Angle {
    submethod TWEAK { $!value %= 400 }
    method Degrees  () { !!! }
    method Radians  () { !!! }
    method Gradians () { self }
}
 class Foo {
     has Angle $.bar is rw;
 }

 my $foo = Foo.new( bar => Degrees.new( 27 ) );
 $foo.bar = Degrees.new( 43 );

关于上一个版本,我希望您注意一些事情。
那里[几乎]没有代码。
它主要是声明性代码,当它出错时往往会更明显。

(您需要使用!!! 填写这些部分,但那里不应有太多代码。)


无论如何,我的观点是,是的,您可以 [使用 Proxy] 做到这一点,但它有一个更困难的原因。
你是从某种程度上违背 Raku 的设计理念的方向来看待问题的。

【讨论】:

  • 亲爱的布拉德 - 感谢您的全面回复。我不热衷于使用 Proxy 方法,因为内置 $.访问器非常非常接近我对最自然的消费单位方式的看法。角度对于大多数单位来说是一个明显的例外——长度、质量等自然具有实数值。我给出的示例是为了删除 Real 类型的值以突出特定的挫败感。我的设计(Physics::Measure)和你的有点不同——但我猜是 TIMTOADY! github.com/p6steve/raku-Physics-Measure
猜你喜欢
  • 1970-01-01
  • 2012-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 1970-01-01
相关资源
最近更新 更多