13 raku
%_据我了解,如果在签名中找不到,则方法的命名参数将转到(不知道为什么!)。为了检测到这一点,我这样做
die "Extra args passed" if %_;
Run Code Online (Sandbox Code Playgroud)
对于我记得要做并且值得做的方法。有没有办法自动执行此操作,例如使用一些装饰器之类的东西?或者我想念并且%_实际上是面向对象的 Raku 编程中非常有用的东西?
\n\n有没有办法自动执行此操作,例如使用一些装饰器之类的东西?
\n
因为在 Raku 中,总是有不止一种方法可以做到这一点,所以实际上有多种方法可以相当轻松地阻止自动*%_方法参数。我将向您展示两个(并解释它们如何工作),然后尝试让您相信,事实上“这实际上%_是面向对象 Raku 编程中非常有用的东西”。
*%_)考虑这段代码:
\nclass C0 { method m { say \'m called\' } }\nclass C1 { method m(*% ()) { say \'m called\' } }\nclass C2 is hidden { method m { say \'m called\' } }\n\nmy $c = C0.new;\n$c.m(:unexpected); # OUTPUT: \xc2\xabm called\xc2\xbb\nRun Code Online (Sandbox Code Playgroud)\nC0是一个标准类,正如您已经注意到的,它很乐意接受意外的命名参数。但如果我们尝试同样的事情C1,我们会得到这个错误:
Unexpected named argument \'unexpected\' passed in sub-signature\nRun Code Online (Sandbox Code Playgroud)\n而且,如果我们尝试使用C2,我们会得到非常相似的结果:
Unexpected named argument \'unexpected\' passed\nRun Code Online (Sandbox Code Playgroud)\n对于我们当前的目的来说,最大的区别是,C1只是不允许m\xe2\x80\x93 的意外命名参数,我们可以创建其他接受意外命名参数的C1方法。相反,使用,我们可以防止任何类方法接受意外的命名参数(并且可以做更多的事情,但很快就会做更多)。C2
C1通过使用命名的 slurpy 参数,然后将该参数解构为子签名\xe2\x80\x93 来防止m接受任何意外参数,在本例中,子签名恰好由空列表组成。
稍微分解一下,考虑一下这段代码:
\nUnexpected named argument \'unexpected\' passed in sub-signature\nRun Code Online (Sandbox Code Playgroud)\n该签名表示“我必须有一个 :group-name 参数和一定数量的 @ids。这些 @ids 必须至少有一个 Int,并且最多可以有 4 个 Int \xe2\x80\x93,但不超过四个,并且没有非整数)”。所以我们可以用f来调用:group-name<foo>, 1。或者与:group-name<foo>, 1, 42, 55. 但如果我们尝试使用 来调用它:group-name<foo>, 1, 42, 55, 9, 9, 9,我们会得到错误:Too many positionals passed to \'f\'; expected 1 to 4 arguments but got 6 in sub-signature of parameter @ids。
C1那么方法中的签名是什么m意思呢?好吧,该*%部分说“我将获取绝对任何命名参数的列表”,然后该 ()部分添加“只要该列表中没有任何内容!”。
这正是您所寻找的:一种拒绝意外命名参数的方法(并且预期的命名参数根本不会造成任何问题 \xe2\x80\x93 您将在 之前声明它们*%,并且它们\会在到达子签名之前得到处理。
C2既简单,同时又有点矫枉过正。 C2使用停止参与redistpatch进程 \xe2\x80\x93 的hidden特征,并且作为副作用,阻止\ 的方法被赋予自动参数。C2C2*%_
解释 Raku 的(重新)调度过程的完整范围超出了这个答案的范围,但我只想说,根据您想要的,这可能正是您正在寻找的,也可能是成为一个不想要的结果。
\n让我们回到你是如何结束你的问题的。你问你是否错过了一些*%_“在面向对象的 Raku 编程中实际上非常有用的东西”。我认为,是的,自动添加*%_实际上非常有帮助。
有很多不同的方法可以解释为什么会出现这种情况,但是,既然你提到了面向对象的 Raku,我将从这个角度来看待它(我个人写了更多函数式编程 Raku,但两者有那里的地方)。
\n因此,从 OOP 的角度来看,添加 使得编写遵循Liskov_substitution_principle(又名,SOLID OOP中的)的 Raku 方法*%_变得更加容易。L
我不确定你对这个原则有多熟悉,所以我只简单说几句话(如果我对显而易见的事情进行了阐述,我深表歉意)。LSP 表示“程序中的对象应该可以用其子类型的实例替换,而不改变该程序的正确性”。
\n这可能有点抽象,所以这里是一个 Raku 示例,\xe2\x80\xa6 好吧,仍然有点抽象,但请耐心等待。
\n因此,考虑一下 RakuStash类。它现在做什么并不重要;它现在做什么并不重要。我之所以提出它是因为它的类型图:
所以 aStash isa Hash和 aHash isa Map和 aMap isa Cool和 a Cool isa Mu。因此,这意味着任何受、、、、 或任何类型约束的函数都将接受\xe2\x80\x93 并且需要确保它可以有效地传递给任何此类函数没有破坏任何东西。当然,大多数情况下 a通过继承来处理这个问题:您可以在 a 上调用的大多数方法实际上并未在 \xe2\x80\x93 上定义,它们是继承的,因此自动与超类兼容方法(因为它们是超类方法)。StashHashMapCoolMuStashStashStashStashStash
但是,对于Stash选择覆盖的方法,Stash确保它不会破坏任何超类的预期行为是工作的一部分。对于 \xe2\x80\x93 来说也是如此,Map但更是如此。如果有一个方法可以Cool覆盖Map,则Map不仅需要考虑Map调用者,还需要考虑Stash调用者:毕竟,Stash可能不会覆盖该方法。因此Map需要确保它可以处理任何Cool想要/需要处理 \xe2\x80\x93 的事情,理论上,这可能随时发生。 (当然,在实践中,Cool它相当稳定,并且它Map都是 Rakudo 的一部分。但同样的情况也适用于第 3 方类,其中 API 变化更快,您可能没有能力协调变化。)
特别是,“处理任何Cool可以处理的东西”意味着能够使用Cool传递的参数调用者进行调用,而不会抛出类型错误 \xe2\x80\x93 即使这些参数可能会发生变化。
这就是*%_出现的地方。除非其中一个选择退出,否则方法链的每个成员都接受任何命名参数。因此,Cool可以自由添加新的命名参数,并且Map/Stash的调用者(毕竟,他们不应该关心或不需要关心他们正在调用哪个对象)可以传入相应的命名参数。而且,即使这些方法调用通过从未听说过新参数的方法得到解决,也绝对不会出现任何问题。
当然,如果它只是被 吃掉*%_,新的参数实际上不会做任何事情,这(取决于类/方法)可能并不理想。但这就是重新分派回来的地方,让干预类对方法进行操作,并仍然将其传递给超类。但是,这又超出了范围。
因此,在对一个简短问题进行长篇回答之后,底线是您可以阻止方法接受意外的命名参数。但是,根据您的编程/Raku 方法,有一些很好的理由考虑不这样做。
\n有没有办法自动执行此操作,例如使用一些装饰器之类的东西?
我目前不知道有什么方法可以做到这一点。
*%_我曾经开发了一种方法特征来从方法签名中删除隐含的内容。希望我可以简化对采用许多不同(组合)命名参数的多种方法的调度。
结局并不好。我已经不记得具体原因了,但我决定推迟尝试这样做,直到 RakuAST 分支落地之后。
或者我想念 %_ 在面向对象的 Raku 编程中实际上是一个非常有用的东西?
我认为,它对于以下内容很有用:
class F {
has $.x;
has $.y;
multi method w-sum (:$x,:$y) { $x*$!x + $y*$!y };
}
class F3 is F {
has $.z;
method w-sum (:$z) { $z*$!z + callsame }
# vs method w-sum (:$x, :$y, :$z) { $z*$!z + callwith( :$x, :$y ) }
}
my F3 $point3D .= new: :2x, :3y, :4z, ;
$point3D.w-sum: :1x, :2y, :3z, ;
Run Code Online (Sandbox Code Playgroud)
我用了%_两次:
w-sum从F3with调用方法x y z,所以
%_={x => 1, y => 2}w-sum从Fby callsamewith调用方法x y z,所以
%_={z => 3}尝试调试版本:
class F {
has $.x;
has $.y;
multi method w-sum(:$x,:$y) { say 'from F: ', %_; $x*$!x + $y*$!y }
}
class F3 is F {
has $.z;
method w-sum (:$z) { say 'from F3: ', %_; $z*$!z + callsame }
# vs method w-sum (:$x, :$y, :$z) { $z*$!z + callwith( :$x, :$y) }
}
my F3 $point3D .= new: :2x, :3y, :4z, ;
say $point3D.w-sum: :1x, :2y, :3z, ;
Run Code Online (Sandbox Code Playgroud)
from F3: {x => 1, y => 2}
from F: {z => 3}
20
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
304 次 |
| 最近记录: |