gpt4 book ai didi

raku - Perl 6 对象如何找到可能在父类或角色中的多方法?

转载 作者:行者123 更新时间:2023-12-03 01:33:40 27 4
gpt4 key购买 nike

考虑这个例子,其中一个子类有一个没有签名的 multi 方法和一个带有 slurpy 参数的方法:

class Foo {
multi method do-it { put "Default" }
multi method do-it ( Int $n ) { put "Int method" }
multi method do-it ( Str $s ) { put "Str method" }
multi method do-it ( Rat $r ) { put "Rat method" }
}

class Bar is Foo {
multi method do-it { put "Bar method" }
multi method do-it (*@a) { put "Bar slurpy method" }
}

Foo.new.do-it: 1;
Foo.new.do-it: 'Perl 6';
Foo.new.do-it: <1/137>;
Foo.new.do-it;

put '-' x 10;

Bar.new.do-it: 1;
Bar.new.do-it: 'Perl 6';
Bar.new.do-it: <1/137>;
Bar.new.do-it: 5+3i;
Bar.new.do-it;

方法查找的结构如何?我正在寻找更多的方法来解释它,特别是不要提示它。
Int method
Str method
Rat method
Default
----------
Int method
Str method
Rat method
Bar slurpy method
Bar method

有一个电话给 Bardo-it1例如。一些理性的人可能会认为它在 Bar 中寻找匹配的签名。首先,那个slurpy永远不会让任何事情过去。然而,该调用会在继承链中找到正确的 multi。

是否 Bar已经知道所有的签名了吗?它是搜索还是所有这些东西在组合时都已经解决了?

而且,有没有办法在运行时找出哪个类提供了该方法?也许有一些呼唤如何?当我有一个错误指定的 multi 并且正在其他地方处理时,这将是一个方便的调试工具。

最佳答案

多分派(dispatch)要记住的关键是它发生在子或方法解析发生之后。所以所有的多重分派(dispatch)实际上是一个两步过程。这两个步骤也是相互独立的。

当写这样的东西时:

multi sub foo($x) { }
multi sub foo($x, $y) { }

编译器会生成一个:
proto sub foo(|) {*}

也就是说,除非你写了一个 proto自己分。 proto实际安装到 lexpad 中的是什么; multi sub 永远不会直接安装到 lexpad 中,而是安装到 proto 的候选列表中。 .

因此,当调用 multi 时子,过程是:
  • 使用词法查找找到要调用的 sub,它解析为 proto
  • 调用 proto ,选择最好的 multi候选人并称之为

  • 当有 multi嵌套作用域中的候选者, proto来自外部作用域的将被克隆并安装到内部作用域中,并将候选者添加到克隆中。

    使用多种方法会发生非常相似的过程,除了:
  • 多个方法只是存储在待办事项列表中,直到结束 }的类、角色或语法
  • 一个 proto可能由角色或类提供,因此使用 multi 组成角色候选人也只是将他们添加到待办事项列表
  • 最后,如果有多个方法没有proto ,但是父类有这样一个 proto ,那将被克隆;否则为空 proto定做

  • 这意味着对多方法的调用是:
  • 使用通常的方法调度算法(仅使用 C3 方法解析顺序搜索类)查找方法,该算法解析为 proto
  • 调用 proto ,选择最好的 multi候选人并称之为

  • 多子和多方法使用完全相同的排序和选择算法。就多重调度算法而言,调用者只是第一个参数。此外,Perl 6 多分派(dispatch)算法不会比较晚的参数更重地加权较早的参数,因此就像:
    class A { }
    class B is A { }
    multi sub f(A, B) { }
    multi sub f(B, A) { }

    如果使用 f(B, B) 调用,将被认为是绑定(bind)的,并给出不明确的调度错误。 ,定义:
    class B { ... }
    class A {
    multi method m(B) { }
    }
    class B is A {
    multi method m(A) { }
    }

    然后拨打 B.m(B) ,因为再次多分配器只看到类型元组 (A, B)(B, A) .

    多重分派(dispatch)本身与狭隘性的概念有关。如果 C1 的至少一个参数是比 C2 中相同位置的参数更窄的类型,并且所有其他参数是绑定(bind)的(即,不是更窄,不是更宽),则候选 C1 比 C2 更窄。如果逆为真,则它更宽。否则,它是绑定(bind)的。一些例子:
    (Int) is narrower than (Any)
    (Int) is tied with (Num)
    (Int) is tied with (Int)
    (Int, Int) is narrower than (Any, Any)
    (Any, Int) is narrower than (Any, Any)
    (Int, Any) is narrower than (Any, Any)
    (Int, Int) is narrower than (Int, Any)
    (Int, Int) is narrower than (Any, Int)
    (Int, Any) is tied with (Any, Int)
    (Int, Int) is tied with (Int, Int)

    multi-dipsatcher 构建候选者的有向图,只要 C1 比 C2 窄,就存在从 C1 到 C2 的边。然后它找到所有没有传入边的候选者,并删除它们。这是第一批候选人。移除将产生一组没有传入边的新候选者,然后将其移除并成为第二组候选者。这一直持续到从图中取出所有候选,或者如果我们达到无法从图中取出任何东西的状态(一种非常罕见的情况,但这将作为循环报告给程序员)。这个过程发生一次,而不是每次调度,它会产生一组候选者。 (是的,它只是一种拓扑排序,但分组细节对于接下来的内容很重要。)

    发生调用时,将搜索组以查找匹配的候选人。如果同一组中的两个候选人匹配,并且没有决胜局(命名参数、 where 子句或隐含的 where 子句来自 subset 类型、解包或 is default ),则将报告不明确的调度.如果搜索所有组都没有找到结果,则调度失败。

    关于 arity(必需参数击败可选参数或 slurpy)和 is rw 也有一些狭隘性考虑。 (它比没有 is rw 的其他相同的候选人更窄)。

    一旦发现一组中的一个或多个候选人匹配,就会考虑决胜局。这些包括命名参数的存在, where条款和解包,并在第一场比赛获胜的基础上工作。
    multi f($i where $i < 3) { } # C1
    multi f($i where $i > 1) { } # C2
    f(2) # C1 and C2 tied; C1 wins by textual ordering due to where

    请注意,此文本排序仅适用于打破平局;就类型而言,源代码中候选的顺序并不重要。 (命名参数也仅作为决胜局有时令人惊讶。)

    最后,我会注意到,虽然多次分派(dispatch)的结果将始终与我描述的两步过程相匹配,但实际上会发生大量的运行时优化。虽然所有查找最初都完全按照描述进行解析,但结果被放入调度缓存中,这提供了比搜索由拓扑排序交付的组更快的查找。这种安装方式可以完全绕过 proto 的调用,从而节省调用帧。如果您 --profile,您可以看到此行为的伪影;自动生成 proto对于任何基于类型的调度(没有 tie-breakers),与多候选者相比,将收到少量调用。如果您在 proto 中编写自定义逻辑,则这不适用。 ,当然。

    除此之外,如果您在 MoarVM 上运行,动态优化器可以走得更远。它可以使用收集和推断的类型信息来解决方法/子分派(dispatch)和多分派(dispatch),将 2 步过程变成 0 步过程。小的候选也可以被内联到调用者中(同样,分析器可以告诉你内联已经发生),这可以说是将多分派(dispatch)变成了 -1 步过程。 :-)

    关于raku - Perl 6 对象如何找到可能在父类或角色中的多方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45047202/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com