【问题标题】:Is that one argument or none for a Perl 6 block?对于 Perl 6 块,这是一个参数还是没有?
【发布时间】:2017-08-01 02:43:57
【问题描述】:

在没有显式签名的块中区分参数和没有参数的 Perl 6 方法是什么?我对此没有任何实际用途,但我很好奇。

没有显式签名的块将值放入$_

my &block := { put "The argument was $_" };

签名实际上是;; $_? is raw。这是一个可选参数。 @_ 变量未在块中定义,因为没有显式签名。

没有参数,$_ 将是未定义的:

&block();  # no argument

但还有一种情况,$_ 将是未定义的。类型对象总是未定义的:

&block(Int);

但是,没有任何内容的$_ 实际上是Any(而不是Nil)。我分不清这两种情况的区别:

&block();
&block(Any);

这是一个更长的例子:

my $block := {
    say "\t.perl is {$_.perl}";

    if $_ ~~ Nil {
        put "\tArgument is Nil"
        }
    elsif ! .defined and $_.^name eq 'Any' {
        put "\tArgument is an Any type object"
        }
    elsif $_ ~~ Any {
        put "\tArgument is {$_.^name} type object"
        }
    else {
        put "\tArgument is $_";
        }
    };

put "No argument: ";    $block();
put "Empty argument: "; $block(Empty);
put "Nil argument: ";   $block(Nil);
put "Any argument: ";   $block(Any);
put "Int argument: ";   $block(Int);

注意没有参数和任何参数形式显示相同的东西:

No argument:
    .perl is Any
    Argument is an Any type object
Empty argument:
    .perl is Empty
    Argument is Slip type object
Nil argument:
    .perl is Nil
    Argument is Nil
Any argument:
    .perl is Any
    Argument is an Any type object
Int argument:
    .perl is Int
    Argument is Int type object

【问题讨论】:

    标签: arguments subroutine raku


    【解决方案1】:

    据我所知,要知道在没有显式签名的情况下传递的参数数量的唯一方法是在正文中使用@_,它将生成 :(*@_) 签名。

    my &block := { say "Got @_.elems() parameter(s)" };
    block;               # Got 0 parameter(s)
    block 42;            # Got 1 parameter(s)
    dd block.signature;  # :(*@_)
    

    是的,好老的@_ 还在,如果你想要的话:-)

    【讨论】:

    • 这是你不想在$_ 中的价值。 @_ 中的值是不可变的。
    • "你不想在 $_ 中赋值。" (如果你在没有明确签名的块中引用@_,我认为brian 的意思是该值不在$_ 中。)然后把它放在那里。例如{ $_ := .[0] if @_ }。是的,这很麻烦。但是 Liz 的回答准确地指出了 brian 在他的问题中提出的问题(“Perl 6 的方法是在没有显式签名的块中区分参数和没有参数的区别?”)。布赖恩的观点是,这意味着您必须手动绑定或分配 $_ 与他接受的答案中的巨大复杂性相比,这是一个小问题。
    • @_ 中的值是不可变的”。仅当它绑定的内容是不可变的。同样的事情也适用于$_。因此{ $_++ }(42) 导致caller postfix:<++> ... require[s] mutable argument。在我之前的评论中,如果您想要可变性,只需将 = 交换为 :=
    【解决方案2】:
    { put $_.perl }
    

    有点类似于这个:(不起作用)

    -> ;; $_? is raw = CALLERS::<$_> { put $_.perl }
    

    由于块外$_ 的默认is defaultAny,如果您在调用函数之前未将任何内容放入$_,则会得到Any


    要获得完全相似的东西,您可以使用Capture

    my &foo = -> ;; |C ($_? is raw) {
        unless C.elems {
           # pretend it was defined like the first Block above
           CALLER::<$_> := CALLER::CALLERS::<$_>
        }
        my $called-with-arguments := C.elems.Bool;
    
    
        if $called-with-arguments {
            say 'called with arguments';
        } else {
            say 'not called with arguments';
        }
    }
    

    【讨论】:

    • 我都知道。我问的是我在其中输入Any 的情况。
    • @briandfoy 如果您需要此信息,请不要使用旨在使差异不可见的功能。
    • 这不是我需要的。我正在研究该语言的工作原理及其边界,以便告诉其他人他们无法做到的事情。
    【解决方案3】:

    这就是我解决这个问题的方法。我很想以一种更简洁的方式来做这件事,但是语言的巧妙性阻碍了我,我必须解决它。这适用于位置参数,但命名参数有更深层次的恶作剧,我不会在这里处理。

    我还有另一个问题Why does constraining a Perl 6 named parameter to a definite value make it a required value?,答案表明实际上没有可选参数。只有具有默认值的参数,如果我没有显式分配一个默认值,就会有一个隐含的默认值。

    我的问题的症结在于我想知道什么时候给参数一个值,什么时候没有。我通过参数或显式默认值给它一个值。隐式默认值是正确类型的类型对象。如果我没有指定类型,那就是Any。该隐式默认值必须满足我指定的任何约束。

    第一个目标是严格限制用户在调用代码时可以提供的值。如果未定义的值无效,则不应允许它们指定一个。

    第二个目标是轻松区分代码中的特殊情况。我想减少一些更深层次的代码需要知道的特殊知识量。

    我可以通过显式分配一个我知道不可能是其他有意义的东西的特殊值来获得第三种情况(我知道没有参数或合适的默认值)。有一个值比Any 更无意义。那是Mu。它是所有未定义值中最未定义的值。 AnyMu 的两个子类型之一(另一个是Junction),但您几乎永远不会看到Mu 在正常代码中出现在您的值之一中。用户代码中未定义的东西从Any开始。

    我可以创建一个约束来检查我想要的类型或Mu,并设置默认值Mu。如果我看到Mu,我知道没有参数,它是Mu,因为我的约束设置了。

    由于我使用的是Mu,所以有些事情我不能做,比如使用=== 运算符。智能匹配不起作用,因为我不想测试继承链。我可以直接查看对象名称:

    my $block := ->
        $i where { $^a.^name eq 'Mu' or $^a ~~ Int:D } = Mu
        {
        say "\t.perl is {$i.perl}";
    
        put do given $i {
            when .^name eq 'Mu'  { "\tThere was no argument" }
            when Nil             { "\tArgument is Nil"       }
            when (! .defined and .^name eq 'Any') {
                "\tArgument is an Any type object"
                }
            when .defined {
                "\tArgument is defined {.^name}"
                }
            default { "\tArgument is {.^name}" }
            }
        };
    
    put "No argument: ";         $block();
    put "Empty argument: ";      try $block(Empty); # fails
    put "Nil argument: ";        try $block(Nil);   # fails
    put "Any type argument: ";   try $block(Any);   # fails
    put "Int type argument: ";   try $block(Int);   # fails
    put "Int type argument: ";   $block(5);
    

    现在大多数调用都失败了,因为它们没有指定正确的东西。

    如果这些是例程,我可以为少数情况制作多个,但最终这是一个更糟糕的解决方案。如果我有两个参数,我需要四个多参数。有了三个这样的参数,我需要六个。这是很多样板代码。但是,块不是例程,所以这里没有实际意义。

    【讨论】:

    • “这就是我解决这个问题的方法。”此答案使用签名。问题是“Perl 6 用什么方法来区分没有显式签名的块中的参数和没有参数的区别?”。这个答案也异常复杂。 Liz 的回答简单、正确,适用于没有明确签名的区块。
    • Liz 的回答不限制参数的数量。我想在没有签名的情况下这样做,但找不到方法(通常有问题和最终答案)。
    • 听从 Liz 的建议并编写像 { fail unless +@_ == 1 } 这样的守卫或您想要的任何其他方案(例如 given +@_ { when 0 { ... }; when 1 { ... }; default { fail } };)有什么问题?如果由于某种原因这仍然不够好并且您必须使用签名,为什么不按照 Brad 的建议只使用 -&gt; |args where { +args == 1 or fail 'nope' } ($arg) { } 或类似名称?
    • 我的回答充分回答了您的 cmets。继续前进。
    猜你喜欢
    • 2017-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-24
    • 1970-01-01
    • 1970-01-01
    • 2017-07-31
    • 1970-01-01
    相关资源
    最近更新 更多