Esta é uma discussão interessante. Eu acho que o exemplo do @ flodel é excelente. No entanto, acho que ilustra meu argumento (e o @koshke menciona isso em um comentário) que return
faz sentido quando você usa um imperativo em vez de um estilo de codificação funcional .
Não para enfatizar a questão, mas eu teria reescrito foo
assim:
foo = function() ifelse(a,a,b)
Um estilo funcional evita alterações de estado, como armazenar o valor de output
. Nesse estilo, return
está fora de lugar; foo
parece mais uma função matemática.
Concordo com o @flodel: usar um sistema complexo de variáveis booleanas bar
seria menos claro e inútil quando você tiver return
. O que torna bar
tão acessível às return
declarações é que elas são escritas em um estilo imperativo. De fato, as variáveis booleanas representam as alterações de "estado" evitadas em um estilo funcional.
É realmente difícil reescrever bar
no estilo funcional, porque é apenas pseudocódigo, mas a ideia é algo como isto:
e_func <- function() do_stuff
d_func <- function() ifelse(any(sapply(seq(d),e_func)),2,3)
b_func <- function() {
do_stuff
ifelse(c,1,sapply(seq(b),d_func))
}
bar <- function () {
do_stuff
sapply(seq(a),b_func) # Not exactly correct, but illustrates the idea.
}
O while
loop seria o mais difícil de reescrever, porque é controlado por alterações de estado em a
.
A perda de velocidade causada por uma chamada para return
é insignificante, mas a eficiência obtida ao evitar return
e reescrever em um estilo funcional é muitas vezes enorme. Dizer aos novos usuários que parem de usar return
provavelmente não ajudará, mas guiá-los para um estilo funcional resultará em recompensa.
@Paul return
é necessário em estilo imperativo, porque muitas vezes você deseja sair da função em diferentes pontos de um loop. Um estilo funcional não usa loops e, portanto, não precisa return
. Em um estilo puramente funcional, a chamada final é quase sempre o valor de retorno desejado.
No Python, as funções requerem uma return
declaração. No entanto, se você programou sua função em um estilo funcional, provavelmente terá apenas uma return
instrução: no final de sua função.
Usando um exemplo de outra postagem StackOverflow, digamos que queremos uma função que retorne TRUE
se todos os valores em um determinado x
tiverem um comprimento ímpar. Nós poderíamos usar dois estilos:
# Procedural / Imperative
allOdd = function(x) {
for (i in x) if (length(i) %% 2 == 0) return (FALSE)
return (TRUE)
}
# Functional
allOdd = function(x)
all(length(x) %% 2 == 1)
Em um estilo funcional, o valor a ser retornado naturalmente cai no final da função. Mais uma vez, parece mais uma função matemática.
@ GSee Os avisos descritos ?ifelse
são definitivamente interessantes, mas não acho que eles estejam tentando dissuadir o uso da função. De fato, ifelse
tem a vantagem de vetorizar funções automaticamente. Por exemplo, considere uma versão ligeiramente modificada de foo
:
foo = function(a) { # Note that it now has an argument
if(a) {
return(a)
} else {
return(b)
}
}
Esta função funciona bem quando length(a)
é 1. Mas se você reescrever foo
com umifelse
foo = function (a) ifelse(a,a,b)
Agora foo
funciona em qualquer comprimento de a
. De fato, funcionaria mesmo quando a
é uma matriz. Retornar um valor da mesma forma que test
um recurso que ajuda na vetorização, não é um problema.
return
é desnecessário mesmo no último exemplo. A remoçãoreturn
pode torná-lo um pouco mais rápido, mas, na minha opinião, isso ocorre porque R é considerado uma linguagem de programação funcional.