Как мне вернуться из анонимной рекурсивной подпрограммы в perl6? - PullRequest
0 голосов
/ 09 февраля 2019

Это делает то, что я ожидал.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 () с помощью анонимного sub

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);

Это последнееверсия никогда не возвращается.Есть ли способ написать анонимную рекурсивную функцию без возвращаемых значений?

Ответы [ 3 ]

0 голосов
/ 09 февраля 2019

Блоки не должны объявлять тип возвращаемого значения .Вы все еще можете вернуть все, что хотите, хотя.Проблема не в использовании 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 вместо каскадных if.Пока все, что он возвращает, является последним выпущенным, все в порядке.

0 голосов
/ 10 февраля 2019

Еще три опции:

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); 
}

компилирует.Надеюсь, вы можете догадаться, что то, что он делает - или, скорее, должен делать - это именно то, что вы хотели сделать.

К сожалению, если вы выполните приведенное выше с помощью:

say $fib(13);

Вы получаете ошибку во время выполнения "оставьте еще не реализованным".

Я предполагаю, что это будет реализовано через несколько лет, и в сообщении об ошибке «Попытка возврата за пределы обычной» будет упоминаться leave.Но его реализация имеет очень низкий приоритет, потому что легко написать sub, как описано выше, или написать код, как это сделал @ HåkonHægland, или использовать конструкцию оператора case / switch следующим образом, и на данный момент этого вполне достаточно.

case / switch (when / default)

Вы можете указать параметр как $_ вместо $x, и тогда вы можете использовать конструкции, которые относятся к теме:

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

say $fib(13); # 233

См. when.

0 голосов
/ 09 февраля 2019

Согласно документации :

Блоки, не относящиеся к типу Routine (являющиеся подклассом Block), прозрачны для возврата.

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
    }
}
...