Hace un tiempo, Simon Urbanek me reprendió del equipo principal de R (creo) por recomendar a un usuario que llamara explícitamente return
al final de una función (aunque su comentario fue eliminado):
foo = function() {
return(value)
}
en cambio él recomendó:
foo = function() {
value
}
Probablemente en una situación como esta se requiere:
foo = function() {
if(a) {
return(a)
} else {
return(b)
}
}
Su comentario arrojó algo de luz sobre por qué no llamar a return
menos que sea estrictamente necesario es algo bueno, pero esto fue eliminado.
Mi pregunta es: ¿por qué no llamar return
más rápido o mejor, y por lo tanto es preferible?
return
es innecesario incluso en el último ejemplo. La eliminaciónreturn
puede hacer que sea un poco más rápido, pero en mi opinión esto se debe a que se dice que R es un lenguaje de programación funcional.return
induce un salto no local, y el salto no local explícito es inusual para FP. En realidad, por ejemplo, el esquema no tienereturn
. Creo que mis comentarios son demasiado cortos (y tal vez incorrectos) como respuesta.return
,break
,continue
o, lo cual es a veces tediosas.Respuestas:
La pregunta era: ¿por qué no (explícitamente) la devolución de llamadas es más rápida o mejor, y por lo tanto preferible?
No hay ninguna declaración en la documentación de R que haga tal suposición.
La página principal? 'Función' dice:
¿Es más rápido sin llamar a return?
Tanto
function()
yreturn()
son funciones primitivas y elfunction()
mismo vuelve último valor evaluado aun sin incluirreturn()
la función.Llamar
return()
como.Primitive('return')
con ese último valor como argumento hará el mismo trabajo pero necesita una llamada más. Para que esta.Primitive('return')
llamada (a menudo) innecesaria pueda atraer recursos adicionales. Sin embargo, la medición simple muestra que la diferencia resultante es muy pequeña y, por lo tanto, no puede ser la razón para no usar el retorno explícito. El siguiente gráfico se crea a partir de los datos seleccionados de esta manera:La imagen de arriba puede diferir ligeramente en su plataforma. Según los datos medidos, el tamaño del objeto devuelto no causa ninguna diferencia, el número de repeticiones (incluso si se amplía) hace una diferencia muy pequeña, que en palabras reales con datos reales y algoritmos reales no podría contarse o hacer que su El script se ejecuta más rápido.
¿Es mejor sin volver a llamar?
Return
es una buena herramienta para diseñar claramente "hojas" de código donde la rutina debe terminar, saltar de la función y devolver el valor.Depende de la estrategia y el estilo de programación del programador, qué estilo usa, no puede usar return () ya que no es necesario.
Los programadores de R core usan ambos enfoques, es decir. con y sin return explícito () como es posible encontrar en fuentes de funciones 'base'.
Muchas veces solo se usa return () (sin argumento) devolviendo NULL en casos para detener la función de manera conditial.
No está claro si es mejor o no, ya que el usuario estándar o el analista que usa R no puede ver la diferencia real.
Mi opinión es que la pregunta debería ser: ¿Existe algún peligro al usar el retorno explícito proveniente de la implementación de R?
O, tal vez mejor, el código de función de escritura del usuario siempre debe preguntar: ¿Cuál es el efecto de no usar el retorno explícito (o colocar el objeto que se devolverá como la última hoja de la rama de código) en el código de función?
fuente
return
, y todo depende de la preferencia del programador de usarlo o no.return
es realmente lo último de lo que debería preocuparse.return
llamadas a funciones innecesarias . La pregunta que debe hacer no es la que propone al final. En cambio, es: "¿por qué debería usar un redundantereturn
? ¿Qué beneficio proporciona? Como resultado, la respuesta es "no mucho", o incluso "ninguno". Su respuesta no puede apreciar esto.return
es como un comentario explícito que dice "incrementar x en 1", al lado de un fragmento de códigox = x + 2
. En otras palabras, su carácter explícito es (a) completamente irrelevante, y (b) transmite la información incorrecta . Debido a quereturn
la semántica en R es, simplemente, "abortar esta función". No , no significa lo mismo quereturn
en otros idiomas.Si todos están de acuerdo en que
return
no es necesario al final del cuerpo de una funciónreturn
es marginalmente más rápido (según la prueba de @ Alan, 4.3 microsegundos versus 5.1)¿deberíamos dejar de usar
return
al final de una función? Ciertamente no lo haré, y me gustaría explicar por qué. Espero saber si otras personas comparten mi opinión. Y me disculpo si no es una respuesta directa al OP, sino más bien como un largo comentario subjetivo.Mi principal problema con el no uso
return
es que, como señaló Paul, hay otros lugares en el cuerpo de una función donde puede necesitarlo. Y si se ve obligado a usarreturn
en algún lugar en el medio de su función, ¿por qué no hacerreturn
explícitas todas las declaraciones? Odio ser inconsistente. También creo que el código se lee mejor; se puede escanear la función y ver fácilmente todos los puntos y valores de salida.Pablo usó este ejemplo:
Desafortunadamente, uno podría señalar que puede reescribirse fácilmente como:
La última versión incluso cumple con algunos estándares de codificación de programación que recomiendan una declaración de retorno por función. Creo que un mejor ejemplo podría haber sido:
Esto sería mucho más difícil de reescribir usando una sola declaración de retorno: necesitaría múltiples
break
sy un intrincado sistema de variables booleanas para propagarlos. Todo esto para decir que la regla de retorno único no juega bien con R. Entonces, si va a necesitar usarreturn
en algunos lugares del cuerpo de su función, ¿por qué no ser coherente y usarlo en todas partes?No creo que el argumento de la velocidad sea válido. Una diferencia de 0,8 microsegundos no es nada cuando comienzas a buscar funciones que realmente hacen algo. Lo último que puedo ver es que escribe menos, pero bueno, no soy flojo.
fuente
return
declaración en algunos casos, como mostró @flodel. Alternativamente, hay situaciones en las que es mejor omitir una declaración de devolución, por ejemplo, muchas y muchas llamadas a funciones pequeñas. En todos los demás, digamos el 95% de los casos, realmente no importa si uno usareturn
o no, y todo se reduce a la preferencia. Me gusta usar return, ya que es más explícito en lo que quieres decir, por lo tanto, más legible. Tal vez esta discusión es similar a<-
vs=
?return
para devolver un valor no tiene sentido, a la par con la escritura enif (x == TRUE)
lugar deif (x)
.foo
comofoo <- function(x) if (a) a else b
(con saltos de línea según sea necesario). No hay necesidad de retorno explícito o valor intermedio.Esta es una discusión interesante. Creo que el ejemplo de @ flodel es excelente. Sin embargo, creo que ilustra mi punto (y @koshke lo menciona en un comentario) que
return
tiene sentido cuando se usa un estilo de codificación imperativo en lugar de uno funcional .No para diferir el punto, pero habría reescrito
foo
así:Un estilo funcional evita cambios de estado, como almacenar el valor de
output
. En este estilo,return
está fuera de lugar;foo
se parece más a una función matemática.Estoy de acuerdo con @flodel: usar un intrincado sistema de variables booleanas en
bar
sería menos claro e inútil cuando lo tienesreturn
. Lo que hacebar
que lasreturn
declaraciones sean tan favorables es que está escrito en un estilo imperativo. De hecho, las variables booleanas representan los cambios de "estado" evitados en un estilo funcional.Es realmente difícil reescribir
bar
en estilo funcional, porque es solo un pseudocódigo, pero la idea es algo como esto:El
while
bucle sería el más difícil de reescribir, porque está controlado por cambios de estado aa
.La pérdida de velocidad causada por una llamada a
return
es insignificante, pero la eficiencia obtenida al evitarreturn
y reescribir en un estilo funcional a menudo es enorme. Decirles a los nuevos usuarios que dejen de usarloreturn
probablemente no ayudará, pero guiarlos hacia un estilo funcional dará resultado.@Paul
return
es necesario en un estilo imperativo porque a menudo desea salir de la función en diferentes puntos de un bucle. Un estilo funcional no usa bucles y, por lo tanto, no es necesarioreturn
. En un estilo puramente funcional, la llamada final es casi siempre el valor de retorno deseado.En Python, las funciones requieren una
return
declaración. Sin embargo, si programó su función en un estilo funcional, es probable que solo tenga unareturn
declaración: al final de su función.Usando un ejemplo de otra publicación de StackOverflow, digamos que deseamos una función que devuelva
TRUE
si todos los valores de un determinadox
tienen una longitud impar. Podríamos usar dos estilos:En un estilo funcional, el valor que se devuelve cae naturalmente en los extremos de la función. De nuevo, se parece más a una función matemática.
@GSee Las advertencias descritas en
?ifelse
son definitivamente interesantes, pero no creo que estén tratando de disuadir el uso de la función. De hecho,ifelse
tiene la ventaja de vectorizar automáticamente las funciones. Por ejemplo, considere una versión ligeramente modificada defoo
:Esta función funciona bien cuando
length(a)
es 1. Pero si reescribiófoo
con unifelse
Ahora
foo
funciona en cualquier longitud dea
. De hecho, incluso funcionaría cuandoa
es una matriz. Devolver un valor con la misma forma quetest
es una característica que ayuda con la vectorización, no es un problema.fuente
return
no encaja con un estilo funcional de programación. Si uno está programando imperativa o funcionalmente, en algún momento una función o subrutina necesita devolver algo. Por ejemplo, la programación funcional en python todavía requiere unareturn
declaración. ¿Podría dar más detalles sobre este punto?ifelse(a,a,b)
es una manía mía. Parece que cada línea?ifelse
está gritando, "no me uses en lugar deif (a) {a} else b
". por ejemplo, "... devuelve un valor con la misma forma quetest
", "siyes
ono
son demasiado cortos, sus elementos se reciclan", "el modo del resultado puede depender del valor detest
", "el atributo de clase del resultado se toma detest
y puede ser inapropiado para los valores seleccionados deyes
yno
"foo
no tiene mucho sentido; siempre devolverá VERDADERO ob
. Usarloifelse
devolverá 1 o varios VERDADEROS, y / o 1 o variosb
s. Inicialmente, pensé que la intención de la función era decir "si alguna declaración es VERDADERA, devuelve algo, de lo contrario, devuelve otra cosa". No creo que debería ser vectorizado, porque entonces se convertiría en "devolver los elementos de un objeto que son verdaderas, y para todos los elementos que no son ciertas, el retornob
.Parece que sin
return()
eso es más rápido ...____ EDITAR__ _ __ _ __ _ __ _ __ _ ___
Procedo a otros benchmark (
benchmark(fuu(x),foo(x),replications=1e7)
) y el resultado se invierte ... Lo intentaré en un servidor.fuente
return()
, una si no lo hace. Es totalmente redundante al final de una función, ya quefunction()
devuelve su último valor. Solo notará esto en muchas repeticiones de una función en la que no se hace mucho internamente, de modo que el costo sereturn()
convierte en una gran parte del tiempo total de cálculo de la función.Un problema con no poner explícitamente 'return' al final es que si uno agrega declaraciones adicionales al final del método, de repente el valor de retorno es incorrecto:
Esto devuelve el valor de
dosomething()
.Ahora llegamos al día siguiente y agregamos una nueva línea:
Queríamos que nuestro código devolviera el valor de
dosomething()
, pero ya no lo hace.Con un retorno explícito, esto se vuelve realmente obvio:
Podemos ver que hay algo extraño en este código y solucionarlo:
fuente
return
no es un atajo, es el estilo apropiado en la programación funcional. El uso dereturn
llamadas a funciones innecesarias es una instancia de programación de culto de carga .return
declaración y no darse cuenta de que no se ejecutará. También podría agregar un comentario después del valor que desea devolver, por ejemplodosomething() # this is my return value, don't add anything after it unless you know goddam well what you are doing
Es más rápido porque
return
es una función (primitiva) en R, lo que significa que su uso en código incurre en el costo de una llamada a función. Compare esto con la mayoría de los otros lenguajes de programación, dondereturn
es una palabra clave, pero no una llamada de función: no se traduce en ninguna ejecución de código de tiempo de ejecución.Dicho esto, llamar a una función primitiva de esta manera es bastante rápido en R, y llamar
return
conlleva una sobrecarga minúscula. Este no es el argumento a favor de la omisiónreturn
.Porque no hay razón para usarlo.
Porque es redundante y no agrega redundancia útil .
Para ser claros: la redundancia a veces puede ser útil . Pero la mayoría de la redundancia no es de este tipo. En cambio, es del tipo que agrega desorden visual sin agregar información: es el equivalente de programación de una palabra de relleno o chartjunk ).
Considere el siguiente ejemplo de un comentario explicativo, que se reconoce universalmente como mala redundancia porque el comentario simplemente parafrasea lo que el código ya expresa:
Usar
return
en R cae en la misma categoría, porque R es un lenguaje de programación funcional y en R cada llamada a función tiene un valor . Esta es una propiedad fundamental de R. Y una vez que vea el código R desde la perspectiva de que cada expresión (incluida cada llamada de función) tiene un valor, la pregunta se convierte en: "¿por qué debería usarreturn
?" Debe haber una razón positiva, ya que el valor predeterminado es no usarlo.Una de estas razones positivas es señalar la salida anticipada de una función, por ejemplo, en una cláusula de guardia :
Este es un uso válido y no redundante de
return
. Sin embargo, tales cláusulas de protección son raras en R en comparación con otros lenguajes, y dado que cada expresión tiene un valor, una regularif
no requierereturn
:Incluso podemos reescribir
f
así:... donde
if (cond) expr
es lo mismo queif (cond) expr else NULL
.Finalmente, me gustaría prevenir tres objeciones comunes:
Algunas personas argumentan que el uso
return
agrega claridad, porque indica "esta función devuelve un valor". Pero como se explicó anteriormente, cada función devuelve algo en R. Pensarreturn
que un marcador de devolución de un valor no es solo redundante, es activamente engañoso .Relacionado, el Zen de Python tiene una pauta maravillosa que siempre debe seguirse:
¿Cómo la caída redundante
return
no viola esto? Porque el valor de retorno de una función en un lenguaje funcional siempre es explícito: es su última expresión. Este es de nuevo el mismo argumento sobre lo explícito frente a la redundancia.De hecho, si desea ser explícito, úselo para resaltar la excepción a la regla: funciones de marca que no devuelven un valor significativo, que solo se llaman por sus efectos secundarios (como
cat
). Excepto R tiene un mejor marcador quereturn
para este caso:invisible
. Por ejemplo, escribiría¿Pero qué hay de las funciones largas? ¿No será fácil perder la noción de lo que se devuelve?
Dos respuestas: primero, no realmente. La regla es clara: la última expresión de una función es su valor. No hay nada que seguir.
Pero lo más importante, el problema en las funciones largas no es la falta de
return
marcadores explícitos . Es el duración de la función . Las funciones largas casi (?) Siempre violan el principio de responsabilidad única e incluso cuando no lo hacen, se beneficiarán de ser separadas para facilitar la lectura.fuente
return
para hacerlo más similar a otros idiomas. Pero ese es un mal argumento: otros lenguajes de programación funcionales tienden a no usarsereturn
tampoco. Solo los lenguajes imperativos , donde no todas las expresiones tienen un valor, lo usan.return
soportes es explícitamente mejor, y leí su respuesta con críticas completas. Su respuesta me ha llevado a reflexionar sobre esa opinión. Creo que la necesidad de usarreturn
explícitamente (al menos en mi propio caso) está vinculada a la necesidad de poder revisar mejor mis funciones en un momento posterior. Con la idea de que mis funciones podrían ser demasiado complejas, ahora puedo ver que un objetivo para mejorar mi estilo de programación sería tratar de estructurar los códigos para mantener la explicidad sin unreturn
. ¡Gracias por esas reflexiones e ideas!Pienso
return
en un truco. Como regla general, el valor de la última expresión evaluada en una función se convierte en el valor de la función, y este patrón general se encuentra en muchos lugares. Todos los siguientes evalúan a 3:Lo que
return
no es realmente devolver un valor (esto se hace con o sin él) sino "romper" la función de manera irregular. En ese sentido, es el equivalente más cercano de la declaración GOTO en R (también hay break y next). Yo usoreturn
muy raramente y nunca al final de una función.... esto se puede reescribir como
if(a) a else b
que es mucho mejor legible y menos cursivo. No hay necesidad dereturn
nada aquí. Mi caso prototípico de uso de "retorno" sería algo así como ...En general, la necesidad de muchos retornos sugiere que el problema es feo o está mal estructurado.
<>
return
realmente no necesita una función para funcionar: puede usarla para salir de un conjunto de expresiones que se evaluarán.fuente
return
(mi feo ejemplo anterior es muy artificial): supongamos que necesita probar si un valor esNULL
oNA
: en estos casos, devuelve una cadena vacía, de lo contrario devuelve elcharacter
valor. Pero una prueba deis.na(NULL)
da un error, por lo que parece que solo se puede hacerif(is.null(x)) return("")
y luego continuarif(is.na(x)) .....
. (Se puede usar enlength(x)==0
lugar de,is.null(x)
pero aún así, no es posible usarlolength(x)==0 | is.na(x)
six
es asíNULL
)|
(OR vectorizado donde se evalúan ambos lados) en lugar de||
(OR cortocircuito, no vectorizado, donde los predicados se evalúan a su vez). Considereif (TRUE | stop()) print(1)
versusif (TRUE || stop()) print(1)
return
puede aumentar la legibilidad del código:fuente
foo <- function() a || b
(que es IMO más legible; en cualquier caso, no hay legibilidad "pura" sino legibilidad en la opinión de alguien: hay personas que dicen que el lenguaje ensamblador es perfectamente legible)El argumento de la redundancia ha surgido mucho aquí. En mi opinión, esa no es razón suficiente para omitir
return()
. La redundancia no es automáticamente una mala cosa. Cuando se usa estratégicamente, la redundancia hace que el código sea más claro y más sostenible.Considere este ejemplo: los parámetros de función a menudo tienen valores predeterminados. Por lo tanto, especificar un valor que sea el mismo que el predeterminado es redundante. Excepto que hace obvio el comportamiento que espero. No es necesario abrir la página de manual de funciones para recordarme cuáles son los valores predeterminados. Y no se preocupe por una versión futura de la función que cambie sus valores predeterminados.
Con una penalización de rendimiento insignificante por llamar
return()
(según los puntos de referencia publicados aquí por otros) se trata de estilo en lugar de correcto e incorrecto. Para que algo esté "mal", debe haber una clara desventaja, y nadie aquí ha demostrado satisfactoriamente que incluir u omitirreturn()
tenga una desventaja constante. Parece muy específico de caso y específico de usuario.Así que aquí es donde estoy parado en esto.
Me siento incómodo con las variables "huérfanas" como en el ejemplo anterior. ¿
abcd
Sería parte de una declaración que no terminé de escribir? ¿Es un remanente de un empalme / edición en mi código y necesita ser eliminado? ¿Accidentalmente pegué / moví algo de otro lugar?Por el contrario, este segundo ejemplo me hace obvio que es un valor de retorno previsto, en lugar de un accidente o un código incompleto. Para mí, esta redundancia no es absolutamente inútil.
Por supuesto, una vez que la función esté terminada y funcionando, podría eliminar la devolución. Pero eliminarlo es en sí mismo un paso adicional redundante y, en mi opinión, es más inútil que incluirlo
return()
en primer lugar.Dicho todo esto, no uso
return()
en resumen las funciones de una línea sin nombre. Allí constituye una gran fracción del código de la función y, por lo tanto, causa un desorden visual que hace que el código sea menos legible. Pero para funciones más grandes formalmente definidas y nombradas, lo uso y probablemente continuaré haciéndolo.fuente
return()
en R no cuesta nada. Es objetivamente redundante, pero ser "inútil" es su juicio subjetivo. Redundante e inútil no son necesariamente sinónimos. Ahí es donde no estamos de acuerdo.return
, y aunque no estoy convencido, creo que es potencialmente válido (definitivamente está en un idioma imperativo ... creo que no se traduce a lenguajes funcionales).