Profundizando en los misterios de la evaluación R ... Esto está estrechamente relacionado con mi pregunta anterior (How to write an R function that evaluates an expression within a data-frame). Digamos que quiero escribir una función topfn
que tome un marco de datos y una expresión que implique nombres de columna de ese marco de datos. Quiero pasar ambos argumentos a otra función fn
que realmente evalúa la expresión dentro del "entorno" del marco de datos. Y quiero tanto fn
y topfn
para que funcione correctamente cuando se pasa una trama de datos y una expresiónR: pasando la expresión a una función interna
Mi primer intento, como se sugiere en la respuesta a la pregunta anterior, es definir:
fn <- function(dfr, expr) {
mf <- match.call()
eval(mf$expr, envir = dfr)
}
topfn
y definir así:
topfn <- function(df, ex) {
mf <- match.call()
fn(df, mf$ex)
}
Ahora bien, si tengo una trama de datos
df <- data.frame(a = 1:5, b = 1:5)
la función interna fn
funciona bien:
> fn(df,a)
[1] 1 2 3 4 5
Pero el topfn
no funciona:
> topfn(df,a)
mf$ex
Para solucionar este primera vez que se comprueba la clase de topfn(df,a)
,
> class(topfn(df,a))
[1] "call"
Esto me da una idea para un hack feo para redefinir fn
de la siguiente manera:
fn <- function(dfr, expr) {
mf <- match.call()
res <- eval(mf$expr, envir = dfr)
if(class(res) == 'call')
eval(expr, envir = dfr) else
res
}
Y ahora ambas funciones de trabajo:
> fn(df,a)
[1] 1 2 3 4 5
> topfn(df,a)
[1] 1 2 3 4 5
Como ya he dicho, esto se ve como un truco feo. ¿Hay una mejor manera (o más lenguaje estándar) para que funcionen? He consultado el documento de normas de evaluación no estándar estándar curiosamente nombrado de Lumley http://developer.r-project.org/nonstandard-eval.pdf, pero no fui particularmente esclarecido después de leerlo. También son útiles los indicadores del código fuente de funciones que puedo consultar para ver ejemplos.
Hay una gran wiki sobre el tema de la evaluación R por Hadley Wickham aquí: https: //github.com/hadley/devtools/wiki/Evaluation –
Y como sugiero, hay que distinguir cuidadosamente entre las funciones que se usan interactivamente y las funciones que son llamados desde otras funciones. Es muy difícil llamar a cualquier función que utilice sustitutos y otros trucos. En otras palabras, no existe una manera limpia para que tanto 'fn' como' topfn' trabajen con los mismos tipos de entradas, y esto es BUENO. – hadley
@hadley Entiendo su punto: las funciones diseñadas para uso interactivo pueden usar sustitutos/otros trucos para que sea fácil escribirlos en la consola (es decir, sin comillas, etc.). Y las funciones que solo están diseñadas para ser llamadas por otras funciones no deben usar esos trucos porque pueden romperse de forma impredecible. En particular, la solución provista por @Richie al final de su respuesta, a pesar de que funciona en mi escenario particular anterior, puede romperse (especialmente 'fn') cuando se usa en otro escenario, creo que eso es lo que tú ' está diciendo, ¿verdad? –