variable una reales real rango grafica funciones funcion ejemplos dominio definicion concepto clases r

reales - Llamar explícitamente a la devolución en una función o no



funciones reales pdf (7)

Hace un tiempo fui criticado por Simon Urbanek del equipo central R (creo) por recomendar a un usuario que return una llamada explícita al final de una función (aunque su comentario fue eliminado):

foo = function() { return(value) }

en cambio, 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 return llamadas a menos que sea estrictamente necesario es algo bueno, pero esto fue eliminado.

Mi pregunta es: ¿por qué el return llamada no es más rápido o mejor y, por lo tanto, es preferible?


Esta es una discusión interesante. Creo que el ejemplo de @flodel es excelente. Sin embargo, creo que ilustra mi punto (y @koshke menciona esto en un comentario) que el return tiene sentido cuando se usa un imperativo en lugar de un estilo de codificación funcional .

No para aclarar el punto, pero habría reescrito foo así:

foo = function() ifelse(a,a,b)

Un estilo funcional evita cambios de estado, como almacenar el valor de output . En este estilo, el return está fuera de lugar; foo parece más a una función matemática.

Estoy de acuerdo con @flodel: usar un intrincado sistema de variables booleanas en la bar sería menos claro e inútil cuando hayas return . Lo que hace que el bar sea ​​tan receptivo a las declaraciones de return 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 la bar en un estilo funcional, porque es solo un pseudocódigo, pero la idea es algo como esto:

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. }

El ciclo while sería el más difícil de reescribir, ya que está controlado por cambios de estado en a.

La pérdida de velocidad causada por una llamada para return es insignificante, pero la eficiencia obtenida al evitar el return y la reescritura en un estilo funcional a menudo es enorme. Decirle a los nuevos usuarios que dejen de usar el return probablemente no ayude, pero guiarlos hacia un estilo funcional dará resultado.

El return @Paul es necesario en 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 necesita return . En un estilo puramente funcional, la llamada final es casi siempre el valor de retorno deseado.

En Python, las funciones requieren una declaración de return . Sin embargo, si programó su función en un estilo funcional, es probable que tenga solo una declaración de return : al final de su función.

Usando un ejemplo de otra publicación de , digamos que queríamos una función que devuelva TRUE si todos los valores en una x determinada tienen una longitud impar. Podríamos usar dos 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)

En un estilo funcional, el valor a devolver naturalmente cae en los extremos de la función. De nuevo, se parece más a una función matemática.

@GVer 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 automáticamente vectorizar funciones. Por ejemplo, considere una versión ligeramente modificada de foo :

foo = function(a) { # Note that it now has an argument if(a) { return(a) } else { return(b) } }

Esta función funciona bien cuando la length(a) es 1. Pero si reescribió foo con ifelse

foo = function (a) ifelse(a,a,b)

Ahora foo funciona en cualquier longitud de a . De hecho, incluso funcionaría cuando a es una matriz. Devolver un valor de la misma forma que la test es una característica que ayuda con la vectorización, no es un problema.


Parece que sin return() es más rápido ...

library(rbenchmark) x <- 1 foo <- function(value) { return(value) } fuu <- function(value) { value } benchmark(foo(x),fuu(x),replications=1e7) test replications elapsed relative user.self sys.self user.child sys.child 1 foo(x) 10000000 51.36 1.185322 51.11 0.11 0 0 2 fuu(x) 10000000 43.33 1.000000 42.97 0.05 0 0

____ EDIT __ _ __ _ __ _ __ _ __ _ ___

benchmark(fuu(x),foo(x),replications=1e7) a otros puntos de referencia ( benchmark(fuu(x),foo(x),replications=1e7) ) y el resultado se invierte ... Lo probaré en un servidor.


Pienso en el return como 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:

local({ 1 2 3 }) eval(expression({ 1 2 3 })) (function() { 1 2 3 })()

Lo que hace el return no es realmente devolver un valor (esto se hace con o sin él) sino "interrumpir" la función de forma irregular. En ese sentido, es el equivalente más cercano de la declaración GOTO en R (también hay ruptura y siguiente). Uso el return muy raramente y nunca al final de una función.

if(a) { return(a) } else { return(b) }

... esto se puede volver a escribir como if(a) a else b que es mucho más legible y menos encorsetado. No hay necesidad de return aquí. Mi caso prototípico de uso de "retorno" sería algo así como ...

ugly <- function(species, x, y){ if(length(species)>1) stop("First argument is too long.") if(species=="Mickey Mouse") return("You''re kidding!") ### do some calculations if(grepl("mouse", species)) { ## do some more calculations if(species=="Dormouse") return(paste0("You''re sleeping until", x+y)) ## do some more calculations return(paste0("You''re a mouse and will be eating for ", x^y, " more minutes.")) } ## some more ugly conditions # ... ### finally return("The end") }

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.

getout <- TRUE # if getout==TRUE then the value of EXP, LOC, and FUN will be "OUTTA HERE" # .... if getout==FALSE then it will be `3` for all these variables EXP <- eval(expression({ 1 2 if(getout) return("OUTTA HERE") 3 })) LOC <- local({ 1 2 if(getout) return("OUTTA HERE") 3 }) FUN <- (function(){ 1 2 if(getout) return("OUTTA HERE") 3 })() identical(EXP,LOC) identical(EXP,FUN)


Si todos están de acuerdo con eso

  1. return no es necesario al final del cuerpo de una función
  2. no usar el return es marginalmente más rápido (según la prueba de @Alan, 4.3 microsegundos contra 5.1)

¿Deberíamos todos dejar de usar el return al final de una función? Ciertamente no lo haré, y me gustaría explicar por qué. Espero escuchar si otras personas comparten mi opinión. Y me disculpo si no es una respuesta directa a la OP, sino más bien como un largo comentario subjetivo.

Mi problema principal al no usar el 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 utilizar el return en algún lugar en el medio de su función, ¿por qué no hacer explícitas todas las declaraciones de return ? Odio ser inconsistente También creo que el código lee mejor; uno puede escanear la función y ver fácilmente todos los puntos de salida y valores.

Pablo usó este ejemplo:

foo = function() { if(a) { return(a) } else { return(b) } }

Desafortunadamente, se podría señalar que se puede reescribir fácilmente como:

foo = function() { if(a) { output <- a } else { output <- b } output }

La última versión incluso se ajusta a algunos estándares de codificación de programación que defienden una declaración de retorno por función. Creo que un mejor ejemplo podría haber sido:

bar <- function() { while (a) { do_stuff for (b) { do_stuff if (c) return(1) for (d) { do_stuff if (e) return(2) } } } return(3) }

Esto sería mucho más difícil de reescribir utilizando una declaración de retorno única: necesitaría varias break y un intrincado sistema de variables booleanas para propagarlas. Todo esto para decir que la regla de devolución única no funciona bien con R. Entonces, si va a necesitar usar el return en algunos lugares del cuerpo de su función, ¿por qué no ser consistente y usarlo en todas partes?

No creo que el argumento de 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 es menos tipeado, pero bueno, no soy flojo.


Un problema con no poner ''return'' explícitamente al final es que si uno agrega declaraciones adicionales al final del método, de repente el valor de retorno es incorrecto:

foo <- function() { dosomething() }

Esto devuelve el valor de dosomething() .

Ahora vamos al día siguiente y agregamos una nueva línea:

foo <- function() { dosomething() dosomething2() }

Queríamos que nuestro código devolviera el valor de dosomething() , pero en cambio ya no lo hace.

Con un retorno explícito, esto se vuelve realmente obvio:

foo <- function() { return( dosomething() ) dosomething2() }

Podemos ver que hay algo extraño en este código y solucionarlo:

foo <- function() { dosomething2() return( dosomething() ) }


return puede aumentar la legibilidad del código:

foo <- function() { if (a) return(a) b }


La pregunta fue: ¿Por qué no (explícitamente) el retorno de llamada es más rápido o mejor y, por lo tanto, es preferible?

No hay ningún enunciado en la documentación de R que haga tal suposición.
Tha man page? ''Function'' dice:

function( arglist ) expr return(value)

¿Es más rápido sin llamar a retorno?

Tanto function() como return() son funciones primitivas y la function() devuelve el último valor evaluado incluso sin incluir la función return() .

Llamar a return() como .Primitive(''return'') con ese último valor como argumento hará el mismo trabajo pero necesita una llamada más. De modo que esta llamada (a menudo) innecesaria .Primitive(''return'') puede 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 utilizar el retorno explícito. El siguiente gráfico se crea a partir de datos seleccionados de esta manera:

bench_nor2 <- function(x,repeats) { system.time(rep( # without explicit return (function(x) vector(length=x,mode="numeric"))(x) ,repeats)) } bench_ret2 <- function(x,repeats) { system.time(rep( # with explicit return (function(x) return(vector(length=x,mode="numeric")))(x) ,repeats)) } maxlen <- 1000 reps <- 10000 along <- seq(from=1,to=maxlen,by=5) ret <- sapply(along,FUN=bench_ret2,repeats=reps) nor <- sapply(along,FUN=bench_nor2,repeats=reps) res <- data.frame(N=along,ELAPSED_RET=ret["elapsed",],ELAPSED_NOR=nor["elapsed",]) # res object is then visualized # R version 2.15

La imagen de arriba puede diferir ligeramente en su plataforma. Según los datos medidos, el tamaño del objeto devuelto no está causando 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 se podía contar o hacer su script ejecutar más rápido.

¿Es mejor sin llamar a devolver?

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.

# here without calling .Primitive(''return'') > (function() {10;20;30;40})() [1] 40 # here with .Primitive(''return'') > (function() {10;20;30;40;return(40)})() [1] 40 # here return terminates flow > (function() {10;20;return();30;40})() NULL > (function() {10;20;return(25);30;40})() [1] 25 >

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 R centrales usan ambos enfoques, es decir. con y sin retorno explícito () ya que es posible encontrarlo en fuentes de funciones ''base''.

Muchas veces solo se usa return () (sin argumento) devolviendo NULL en casos para detener la función.

No está claro si es mejor o no como usuario estándar o analista que usa R no puede ver la diferencia real.

Mi opinión es que la pregunta debería ser: ¿hay algún peligro al usar el retorno explícito proveniente de la implementación de R?

O, mejor aún, el código de la 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 del código) en el código de la función?