【问题标题】:How do I return from an anonymous recursive sub in perl6?如何从 perl6 中的匿名递归子返回?
【发布时间】:2019-02-09 19:14:12
【问题描述】:

这符合我的预期。 fib(13) 返回 233。

sub fib(Int $a --> Int) {
    return 0 if $a == 0;
    return 1 if $a == 1;

    return fib($a -1) + fib($a -2);
}

my $square = -> $x { $x * 2 };   # this works with no return value
my @list = <1 2 3 4 5 6 7 8 9>.map( $square );
# returns [2 4 6 8 10 12 14 16 18]

我尝试使用匿名子实现 fib()

my $fib = -> Int $x --> Int {
    return 0 if $x == 0;
    return 1 if $x == 1;
    return $fib($x - 1) + $fib($x - 2); 
}

$fib(13) 

使用显式返回运行时出现以下错误。

尝试在任何例程之外返回 在 test.p6 第 39 行的块中

所以我去掉了返回值。

my $fib = -> Int $x --> Int {
    0 if $x == 0;
    1 if $x == 1;
    $fib($x - 1) + $fib($x - 2); 
}

say $fib(13);

最后一个版本永远不会返回。有没有办法写一个没有返回值的匿名递归函数?

【问题讨论】:

  • 注意可以使用&amp;?BLOCK来引用当前块,这样就可以调用了。 -&gt; Int $_ --&gt; Int { when 0 {1}; when 1 {1}; &amp;?BLOCK($_-1) + &amp;?BLOCK($_-2) }

标签: raku


【解决方案1】:

根据the documentation

不属于常规类型的块(它是块的子类)是 透明返回。

sub f() {
say <a b c>.map: { return 42 };
               #   ^^^^^^   exits &f, not just the block  }

最后一条语句是块的隐式返回值

所以你可以试试:

my $fib = -> Int $x --> Int {
    if ( $x == 0 ) {
        0;  # <-- Implicit return value
    }
    elsif ( $x == 1 ) {
        1;  # <-- Implicit return value
    }
    else {
        $fib($x - 1) + $fib($x - 2);  # <-- Implicit return value
    }
}

【讨论】:

  • 隐式形式很漂亮,尽管可能值得一提的另一个选项是写sub (Int $x --&gt; Int) { ... } - 即,一个真正的匿名子 - 其余的按照最初编写的方式工作。
【解决方案2】:

另外三个选项:

sub

您可以使用不带名称的sub 编写匿名例程

my $fib = sub (Int $x --> Int) {
  return 0 if $x == 0;
  return 1 if $x == 1;
  return $fib($x - 1) + $fib($x - 2); 
}

say $fib(13); # 233

请参阅@HåkonHægland 的回答,了解为什么这(故意)不适用于非常规块。

leave

设计预期您的问题:

my $fib = -> Int $x --> Int {
  leave 0 if $x == 0;
  leave 1 if $x == 1;
  leave $fib($x - 1) + $fib($x - 2); 
}

编译。希望你能猜到它的作用——或者更确切地说是is supposed to do——正是你想要做的。

很遗憾,如果您遵循上述规定:

say $fib(13);

您收到运行时错误“未实现”。

我的猜测是,这将在未来几年的某个时间实施,然后“尝试在任何例程之外返回”错误消息将提到 leave。但是实现它的优先级很低,因为很容易像上面那样编写sub,或者像@HåkonHægland那样编写代码,或者使用如下的case/switch语句构造,现在已经足够好了。

案例/开关(when/default

您可以将参数指定为$_ 而不是$x,然后您就可以使用引用该主题的构造了:

my $fib = -> Int $_ --> Int {
  when 0 { 0 }
  when 1 { 1 }
  $fib($_ - 1) + $fib($_ - 2)
}

say $fib(13); # 233

when

【讨论】:

    【解决方案3】:

    Blocks don't need to declare the return type。不过,你仍然可以返回任何你想要的东西。问题不在于使用 return,而在于 Int 的声明。

    use v6;
    
    my $fib = -> Int $x  {
        if $x == 0 {
            0;
        } elsif $x == 1 {
            1;
        } else {
            $fib($x - 1) + $fib($x - 2);
        }
    }
    
    say $fib(13) ;
    

    问题是返回值需要是最后执行的。以您所做的方式,如果它找到 0 或 1,它会继续运行,到达最后一条语句,然后重新开始。 或者,您可以使用 given 而不是级联 ifs。只要它返回的是最后一个发出的,就可以了。

    【讨论】:

    • "块没有声明返回类型......问题不在于使用return,而在于Int的声明。"您的代码将与留下的 --&gt; Int 一起使用(如果更改为 --&gt; Str,则会失败)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-27
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    • 2022-06-20
    • 1970-01-01
    • 2022-01-25
    相关资源
    最近更新 更多