Por que a troca explícita fazer a diferença em um Proc?
-
07-07-2019 - |
Pergunta
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"
Eu pensei que o return
palavra-chave era opcional em Ruby e que você está sempre return
ing se você solicitá-lo ou não. Dado que, acho que é surpreendente que foo
e bar
tem saída diferente determinada pelo fato de que foo
contém um return
explícito na Proc f
.
Alguém sabe por que este é o caso?
Solução
Ruby tem três construções:
- A bloco não é um objeto e é criado por
{
...}
oudo
...end
. - A proc é um objeto
Proc
criado porProc.new
ouproc
. - A lambda é um
Proc
criado porlambda
(ouproc
no Ruby 1.8).
Ruby tem três palavras-chave que o retorno de algo:
-
return
termina o método ou lambda é no. -
next
termina o bloco, proc, ou lambda é no. -
break
termina o método que passado para o bloco ou o chamado proc ou lambda é em.
Em lambdas, se comporta return
como next
, por qualquer motivo. next
e break
são nomeados a forma como eles são, porque eles são mais comumente usados ??com métodos como each
, onde encerra o bloco fará com que a iteração para retomada na próxima elemento da coleção, e que encerra each
causará você pausa fora do circuito.
Se você usar
return
dentro da definição de foo
, você vai voltar a partir foo
, mesmo que seja dentro de um bloco ou de um proc. Para retornar de um bloco, você pode usar a palavra-chave next
vez.
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"
Outras dicas
Esta é a semântica para Proc
s; não é necessariamente a semântica para todos os blocos. Eu concordo que isso é um pouco confuso. É ali para flexibilidade adicional (e talvez parcialmente causa Ruby tem nenhuma especificação, excepto para a sua execução).
O comportamento é definido na implementação Proc
. Lambda
s comportar de maneira diferente, por isso, se você gostaria que seus return
s não a saída para fora do método delimitador, uso lambdas . Ou, omitir a palavra-chave return
do seu Proc
.
A investigação profunda de Rubys fechamentos é aqui . É uma exposição fantástica.
Assim:
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)
Pense nisso desta maneira: Proc.new apenas criar um bloco de código que faz parte da função de chamada. proc / LAMBDA criar uma função anônima que tem ligações especiais. A exemplos de código pouco vai ajudar:
def foo
f = Proc.new { return "return from foo from inside Proc.new" }
f.call # control leaves foo here
return "return from foo"
end
é equivalente a
def foo
begin
return "return from foo from inside begin/end" }
end
return "return from foo"
end
por isso, é claro que o retorno só vai retornar da função 'foo'
em contraste:
def foo
f = proc { return "return from foo from inside proc" }
f.call # control stasy in foo here
return "return from foo"
end
é equivalente a (ignorando as ligações, uma vez não utilizado neste exemplo):
def unonymous_proc
return "return from foo from inside proc"
end
def foo
unonymous_proc()
return "return from foo"
end
O que é tão claramente não voltará a partir de foo e continuar para a próxima instrução em vez disso.