【问题标题】:Does an anonymous parameter in a Perl 6 signature discard the value?Perl 6 签名中的匿名参数是否会丢弃该值?
【发布时间】:2016-12-22 19:44:54
【问题描述】:

在 Perl 6 Signature docs 中,有一个匿名 slurpy 参数的示例:

sub one-arg (@)  { }
sub slurpy  (*@) { }
one-arg (5, 6, 7); # ok, same as one-arg((5, 6, 7))
slurpy  (5, 6, 7); # ok
slurpy   5, 6, 7 ; # ok

子程序中没有语句,主要是因为这周围的文字是关于满足签名的参数列表,而不是子程序对它的处理。

我正在使用它并尝试制作一个子例程,该子例程包含多个项目之一的列表(因此,不是零项目)。我并没有特别在意他们的名字。我想我仍然可以访问@_ 中的参数列表,即使有签名。但是,当您没有签名时,您会得到@_

$ perl6
To exit type 'exit' or '^D'
> sub slurpy(*@) { say @_ }
===SORRY!=== Error while compiling:
Placeholder variable '@_' cannot override existing signature
------> sub⏏ slurpy(*@) { say @_ }

是否有另一种获取参数列表的方法,或者匿名参数是否会丢弃它们?我看到它们在类型约束部分中使用,但没有使用任何参数值的示例。我还能得到参数列表吗?

【问题讨论】:

    标签: signature raku


    【解决方案1】:

    值不会被丢弃;例如,您可以通过nextsame 访问它:

    multi access-anon(*@) is default {
        say "in first candidate";
        nextsame;
    }
    multi access-anon(*@a) {
        @a;
    }
    say access-anon('foo')
    

    输出:

    in first candidate
    [foo]
    

    但要达到您的原始目标(一个至少包含一个元素的数组),您实际上并不需要访问该列表;您可以使用子签名:

    sub at-least-one(@ [$, *@]) { }
    at-least-one([1]);   # no error
    at-least-one([]);    # Too few positionals passed; expected at least 1 argument but got only 0 in sub-signature
    

    【讨论】:

    • 在你的至少一个例子中,你不是回到同样的问题吗?在这些潜艇中,您仍然无法访问参数。 nextsame 很有趣,但我并不是在寻找那种级别的魔法。
    • @briandfoy:对。但是,如果您将第一个 @ 命名为 @_,如我的答案底部所示,您将有效地得到您想要做的事情:有权访问 @_ 中的所有参数,而无需命名子签名的$, *@ 参数仅用于强制执行“一个或多个项目”条件。
    • 你在说什么问题?如果你想访问变量,只需给它一个名字:-)。如果需要,您还可以访问整个参数列表,但您还必须为其命名。即使在 Perl 5 中,您也必须有一个名称 (@_) 才能访问内容。
    • 我只是在探索匿名参数。我并不缺乏任务的解决方案。
    • 我认为子签名的东西最接近我应该怎么做,但我以后会考虑更多。
    【解决方案2】:

    Perl 6 签名中的匿名参数是否丢弃该值?

    是的,除非您使用函数签名中的某个命名参数来捕获参数,否则它在函数体中将不可用。 (PS:正如莫里茨的回答所示,它们并没有真正被丢弃。)

    我认为即使有签名,我仍然可以访问 @_ 中的参数列表。

    @_ 不能替代使用参数 - 它参数。

    每个函数都有一个明确定义的签名来指定它的参数,它们代表了函数体获取函数传递的值的唯一机制。

    只是声明函数签名有三种不同的方式:

    1. 如果您明确写出签名,则使用该签名。
    2. 如果您在函数体中使用 占位符参数@_$^foo 等),编译器会使用该信息为您构建签名:
      say { $^a + @_ }.signature; # ($a, *@_)
    3. 如果上述都不是,则签名变为:
      • 如果是子例程,则接受零参数:
        say (sub { 42 }).signature; # ()
      • 在裸块的情况下,一个接受零或一个参数的块,可用作$_
        say { 42 }.signature; # (;; $_? 是原始的)

    在所有情况下,函数最终都会在编译时得到明确的签名。 (尝试在已经具有显式签名的函数中使用 $^foo 是编译时错误。)

    还有其他方法可以获取参数列表

    确保使用非匿名参数捕获它。如果您希望它可以作为@_ 访问,那么在您正在编写的显式签名中调用它。

    我 [...] 试图创建一个包含多个项目列表的子例程

    您可以为此使用子签名:

    sub foo (*@_ [$, *@]) { ... };
    

    或者一个where约束:

    sub foo (*@_ where .so) { ... };
    

    【讨论】:

    • FWIW, .elems > 0 不是很好,因为它会导致一些事情被完全具体化。最好使用.so,只需检查布尔值,最多需要具体化一项。
    • @ZoffixZnet:已更新。
    【解决方案3】:

    要填充@_,签名必须是隐式或显式的,不能同时使用。

    sub implicit             {      say @_ } # most like Perl 5's behaviour
    sub explicit     ( *@_ ) {      say @_ }
    sub placeholder          { $^a; say @_ }
    

    这也适用于方块

    my &implicit    =        {      say @_ }
    my &explicit    = -> *@_ {      say @_ }
    my &placeholder =        { $^a, say @_ }
    

    块也可以有一个隐式参数$_,但如果有@_,则优先。

    {     say $_ }(5) # 5
    $_ = 4;
    { @_; say $_ }(5) # 4
    

    这样做是有道理的,因为一个程序员可能认为它的工作方式与你认为的一样,或者它像隐含的那样是糊涂的,而另一个程序员可能认为它得到了所有剩余的参数。

    sub identical (     @_           ) { say @_ }
    sub slurpy    (    *@_ ( @, *@ ) ) { say @_ } # same as implicit
    sub slurpy2   (   **@_ ( @, *@ ) ) { say @_ } # non-flattening
    sub remaining ( @, *@_           ) { say @_ }
    
    identical  [1,2];       # [1 2]
    slurpy    $[1,2],3,4,5; # [[1 2] 3 4 5]
    slurpy2    [1,2],3,4,5; # [[1 2] 3 4 5]
    remaining  [1,2],3,4,5; # [3 4 5]
    

    @_ 也可以作为错误添加,在这种情况下,最好让它产生错误。


    如果不声明捕获参数,就无法获取原始参数。

    sub raw-one    ( |capture ( @ ) ) { capture.perl }
    sub raw-slurpy ( |capture, *@   ) { capture.perl }
    
    raw-one    [1,2]; # \([1, 2])
    raw-slurpy  1,2 ; # \(1, 2)
    

    【讨论】:

    • 那么匿名参数是否隐藏了参数并且无法访问?
    • @briandfoy 嗯......可能有一种方法可以在 Rakudo 中使用 nqp 来解决它们,但是 Perl 6 没有明确的方法来做到这一点。您也许还可以向子组件添加一个特征,让您这样做,并且应该与编译器无关。
    • 您不必使用子签名进行捕获。 IE。这也有效:`sub raw (|capture, *@) { say capture }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-09
    • 2015-05-02
    • 1970-01-01
    • 2015-07-30
    • 2012-12-19
    • 1970-01-01
    相关资源
    最近更新 更多