Pourquoi le retour explicite fait-il une différence dans un Proc?
-
07-07-2019 - |
Question
def foo
f = Proc.new { return "return from foo from inside proc" }
f.call # control leaves foo here
return "return from foo"
end
def bar
b = Proc.new { "return from bar from inside proc" }
b.call # control leaves bar here
return "return from bar"
end
puts foo # prints "return from foo from inside proc"
puts bar # prints "return from bar"
Je pensais que le mot-clé return
était facultatif dans Ruby et que vous foo
et bar
aient une sortie différente, déterminée par le fait que foo
contient un retour
dans Proc f
.
Quelqu'un sait-il pourquoi c'est le cas?
La solution
Ruby a trois constructions:
- Un bloc n'est pas un objet et est créé par
{
...}
oudo
. .end
. - Un proc est un objet
Proc
créé parProc.new
ouproc
. - Un lambda est un
Proc
créé parlambda
(ouproc
dans Ruby 1.8).
Ruby a trois mots clés qui retournent quelque chose:
-
return
met fin à la méthode ou à la méthode lambda dans laquelle il se trouve. -
next
termine le bloc, proc ou lambda dans lequel il se trouve. -
break
met fin à la méthode ayant abouti au bloc ou appelé le proc ou le lambda dans lequel il se trouve.
Dans lambdas, return
se comporte comme suivant
, quelle qu'en soit la raison. next
et break
sont nommés tels quels car ils sont le plus souvent utilisés avec des méthodes telles que each
, où la fermeture du bloc entraînera l'itération reprenez avec l’élément next de la collection, et terminer chaque
vous fera interrompre la boucle.
Si vous utilisez
return
dans la définition de foo
, vous reviendrez de foo
, même s'il se trouve dans un bloc ou un proc. Pour revenir d'un bloc, vous pouvez utiliser le mot-clé next
à la place.
def foo
f = Proc.new { next "return from foo from inside proc" }
f.call # control leaves foo here
return "return from foo"
end
puts foo # prints "return from foo"
Autres conseils
Ceci est la sémantique de Proc
s; ce n'est pas nécessairement la sémantique de tous les blocs. Je suis d'accord c'est un peu déroutant. Il est là pour plus de flexibilité (et peut-être en partie parce que Ruby n’a pas de spécifications à part sa mise en œuvre).
Le comportement est défini dans l'implémentation Proc
. Lambda
se comporte différemment, donc si vous souhaitez que votre renvoie
ne quitte pas la méthode englobante, utilisez lambdas . Sinon, omettez le mot clé return
de votre Proc
.
Une enquête approfondie sur les fermetures de Rubys est . C’est une exposition fantastique & # 233;.
Donc:
def foo
f = Proc.new {
p2 = Proc.new { return "inner proc"};
p2.call
return "proc"
}
f.call
return "foo"
end
def foo2
result = Proc.new{"proc"}.call
"foo2 (proc result is: #{result})"
end
def bar
l = lambda { return "lambda" }
result = l.call
return "bar (lambda result is: #{result})"
end
puts foo
# inner proc
puts foo2
# foo (proc result is: proc)
puts bar
# bar (lambda result is: lambda)
Pensez-y de cette façon: Proc.nouveau vient de créer un bloc de code faisant partie de la fonction appelante. proc / lambda crée une fonction anonyme comportant des liaisons spéciales. Quelques exemples de code aideront:
def foo
f = Proc.new { return "return from foo from inside Proc.new" }
f.call # control leaves foo here
return "return from foo"
end
est équivalent à
def foo
begin
return "return from foo from inside begin/end" }
end
return "return from foo"
end
il est donc clair que le retour va simplement revenir de la fonction 'foo'
en revanche:
def foo
f = proc { return "return from foo from inside proc" }
f.call # control stasy in foo here
return "return from foo"
end
est équivalent à (en ignorant les liaisons car non utilisé dans cet exemple):
def unonymous_proc
return "return from foo from inside proc"
end
def foo
unonymous_proc()
return "return from foo"
end
Ce qui est aussi clair ne reviendra pas de foo et passera à l'instruction suivante.