Cela fait ce à quoi je m'attendais. fib (13) renvoie 233.
my $fib = -> Int $x --> Int { 0 if $x == 0; 1 if $x == 1; $fib($x - 1) + $fib($x - 2); } say $fib(13);
J'ai essayé d'implémenter fib () en utilisant un sous anonyme
my $fib = -> Int $x --> Int { return 0 if $x == 0; return 1 if $x == 1; return $fib($x - 1) + $fib($x - 2); } $fib(13)
J'obtiens l'erreur suivante lors de l'exécution cela avec des retours explicites.
Tentative de retour en dehors de toute routine en bloc à test.p6 ligne 39
Donc je me suis débarrassé des valeurs de retour.
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]
Cette dernière version ne revient jamais. Existe-t-il un moyen d'écrire une fonction récursive anonyme sans valeurs de retour?
3 Réponses :
D'après la documentation :
Les blocs qui ne sont pas de type Routine (qui est une sous-classe de Block) sont transparent pour retourner.
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 } }La dernière instruction est la valeur de retour implicite du bloc
Vous pouvez donc essayer:
sub f() { say <a b c>.map: { return 42 }; # ^^^^^^ exits &f, not just the block }
La forme implicite est jolie, même si une autre option qui mérite peut-être d'être mentionnée est d'écrire sub (Int $ x -> Int) {...}
- c'est-à-dire un véritable sous-anonyme - et le reste fonctionne comme il a été écrit à l'origine.
Les blocs n'ont pas besoin de déclarer le type de retour . Cependant, vous pouvez toujours rendre ce que vous voulez. Le problème n'est pas d'utiliser return, c'est dans la déclaration de l'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) ;
Le problème est que la valeur de retour doit être la dernière exécutée. De la façon dont vous l'avez fait, s'il trouve 0 ou 1, il continue de fonctionner, atteignant la dernière instruction, quand il recommencera.
Vous pouvez également utiliser given
au lieu des ifs en cascade . Tant que ce qu'il renvoie est le dernier émis, c'est OK.
"Les blocs ne déclarent pas le type de retour. ... Le problème n'est pas d'utiliser return
, c'est dans la déclaration de Int
." Votre code fonctionnera avec le -> Int
laissé dans (et échouera s'il est changé, par exemple, -> Str
).
Trois autres options:
sub
Vous pouvez écrire des routines anonymes en utilisant sub
sans nom :
my $fib = -> Int $_ --> Int { when 0 { 0 } when 1 { 1 } $fib($_ - 1) + $fib($_ - 2) } say $fib(13); # 233
Voir la réponse de @ Hà ¥ konHægland pour savoir pourquoi cela (délibérément) ne fonctionne pas avec des blocs non routiniers.
quitter
Le design a anticipé votre question:
say $fib(13);
compile. J'espère que vous pouvez deviner que ce qu'il fait - ou plutôt est censé faire - est exactement ce que vous vouliez faire.
Malheureusement, si vous suivez ce qui précède avec:
my $fib = -> Int $x --> Int { leave 0 if $x == 0; leave 1 if $x == 1; leave $fib($x - 1) + $fib($x - 2); }
Vous obtenez une erreur d'exécution "laissez pas encore implémenté ".
Je suppose que cela sera mis en œuvre dans les prochaines années et le message d'erreur" Tentative de retour en dehors de toute routine "mentionnera alors congé
. Mais sa mise en œuvre a une très faible priorité car il est facile d'écrire sub
comme ci-dessus, ou d'écrire du code comme @ Hà ¥ konHægland l'a fait, ou d'utiliser une construction d'instruction case / switch comme suit, et c'est très bien assez pour le moment.
when
/ default
) Vous pouvez spécifier le paramètre comme $ _
au lieu de $ x
et vous êtes prêt à utiliser des constructions qui font référence au sujet:
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
Voir quand
.
Notez que vous pouvez utiliser
&? BLOCK
pour faire référence au bloc actuel afin de pouvoir l'appeler.-> Int $ _ -> Int {quand 0 {1}; quand 1 {1}; &? BLOC ($ _- 1) + &? BLOC ($ _- 2)}