【问题标题】:What is the difference between using Raku's Code.assuming method and using an anonymous Block or Sub?使用 Raku 的 Code.sumption 方法和使用匿名 Block 或 Sub 有什么区别?
【发布时间】:2021-03-14 15:11:14
【问题描述】:

Raku docs say那个Code.assuming

返回一个 Callable,它实现与原始行为相同的行为,但传递给 .假设的值已经绑定到相应的参数。

使用.assuming 与将代码包装在匿名块(或子)中调用内部函数并绑定一些参数有什么区别?

例如,在下面的代码中,&surname-public(文档为.assuming 提供的示例)和&surname-block 之间有什么区别;


sub longer-names ( $first, $middle, $last, $suffix ) {
    say "Name is $first $middle $last $suffix";
}

my &surname-public = &longer-names.assuming( *, *, 'Public', * );
my &surname-block  = -> $a,$b,$c { longer-names($a, $b, 'Public', $c) }

surname-public( 'Joe', 'Q.', 'Jr.');  # OUTPUT: «Name is Joe Q. Public Jr.»
surname-block(  'Joe', 'Q.', 'Jr.');  # OUTPUT: «Name is Joe Q. Public Jr.»

我看到.assuming 节省了一些篇幅,并且在某些情况下可以更清晰一些。但我强烈怀疑我遗漏了其他一些差异。

【问题讨论】:

  • “节省一点长度”,“更清晰一点”。另外:❸静态编码意图。原则上.assume 有朝一日可能成为一个宏,它在编译时进行相关的优化,可靠地超过 JIT 可以做的事情。也许。 ❹ 保留原始签名。例如,如果更改了参数的名称或类型约束,则例程的柯里化版本将自动跟踪这些更改,这可能(通常是)对于参数类型/名称的编译器(Rakudo)强制执行而言是最好的和工具(例如 CommaIDE)显示签名。
  • @raiph 你很可能是正确的,这只是这 4 个差异。但是,我想知道…… Code.假设 source 大约有 275 行,这似乎需要花很多钱来减少到关闭。我还没有深入挖掘源头——尽管如果没有人知道答案,我会在有时间的时候挖掘——但这让我想知道它是否还有更多。
  • 大概代码必须进行自定义的、完全手动的自省/元编程签名绑定的复制 自定义的、完全手动的元编程构造一个新的签名,以及一个带有该签名的新闭包。绑定仿真需要遍历原始签名中的每个参数,注意哪些.assuming 参数已被传递,哪些被省略,哪些是*(也许还有** 变体?)。构造也可能很冗长。而且我想错误消息可能是无数和/或复杂的构造,以便不是 LTA。
  • sub work($i, :$debug) { say $i if $debug; return $i }; my &w1 = &work.assuming(1); my &w2 = &w1.assuming(debug => True); w1(); w2(); 当您开始考虑可选的命名参数时,链接等柯里化与assuming相比变得相当冗长
  • ".assuming 的当前实现 - 用于对例程进行 curry 或其他主要参数 - 并不理想,利用新调度程序的参数重写功能的实现可能会执行好多了”(来自Towards a new general dispatch mechanism in MoarVM

标签: raku currying rakudo


【解决方案1】:

真的没有区别。

虽然实现.assuming() 的代码将近 300 行,但重要的一点只有大约 10 行代码。

    $f = EVAL sprintf(
        '{ my $res = (my proto __PRIMED_ANON (%s) { {*} });
           my multi __PRIMED_ANON (|%s(%s)) {
               my %%chash := %s.hash;
               $self(%s%s |{ %%ahash, %%chash });
           };
           $res }()',
        $primed_sig, $capwrap, $primed_sig, $capwrap,
        (flat @clist).join(", "),
        (@clist ?? ',' !! '')
    );

.assuming 中的其余代码主要是关于从签名中提取信息。


让我们将您的代码插入到sprintf
(不完全相同,但对于我们的目的来说已经足够接近了。)

{
  my $res = (
    #  $primed_sig          v----------------------v
    my proto __PRIMED_ANON ($first, $middle, $suffix) { {*} }
  );

  # $capwrap               vv
  # $primed_sig                v----------------------v
  my multi __PRIMED_ANON (|__ ($first, $middle, $suffix)) {
    # $capwrap   vv
    my %chash := __.hash;

    #     v---------------------------v  @clist
    $self(__[0], __[1], 'Public', __[2], |{ %ahash, %chash });
  };

  # return the proto
  $res
}()

如果我们简化它,并根据您的代码定制它

my &surname-public = {
  my $res = (
    my proto __PRIMED_ANON ($first, $middle, $suffix) { {*} }
  );

  my multi __PRIMED_ANON ( $first, $middle, $suffix ) {
    longer-names( $first, $middle, 'Public', $suffix )
  };

  $res
}()

我们可以通过使用一个尖头块来进一步简化它。

my &surname-public = -> $first, $middle, $suffix {
  longer-names( $first, $middle, 'Public', $suffix )
};

也可以使用单字母参数名称。

my &surname-public = -> $a,$b,$c { longer-names($a, $b, 'Public', $c) }

就像我说的,真的没有区别。


未来,使用.assuming() 可能会更有益。在它被重写以使用 RakuAST 之后。

【讨论】:

  • 非常有趣,谢谢。您提到,“将来,使用.assuming() 可能会更有益。在它被重写为使用 RakuAST 之后。”现在怎么样?看到EVAL 的调用让我觉得使用尖头块目前比assuming很多,但这可能是一个没有根据的假设。
  • @codesections 目前使用.assuming() 的唯一好处是它很容易。 (我确信它目前要慢得多。)例如,使用 RakuAST,它可以通过不创建块来作弊。
  • 另外,对我来说最简单的形式是隐式变量:my &surname-public = { longer-name $^a, $^b, 'Public', $^c }
  • 哦,我非常喜欢带有正式/隐式变量的版本!而且那个比.assuming 更短/更清晰,所以似乎没有任何理由使用.assuming,除非/直到它被优化到足以显着更快。
  • @codesections 我想说.assuming 在常见情况下更加简洁易读,例如当一个人假设多个参数中间只有一个参数时。
猜你喜欢
  • 2018-05-02
  • 1970-01-01
  • 1970-01-01
  • 2017-09-09
  • 2013-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多