2012-06-05 13 views
6

considerar esta función a(), que imprime el argumento que se aprobó en:sustituto() para obtener nombres de argumentos, múltiples niveles de hasta

a <- function(x) { 
    message("The input is ", deparse(substitute(x))) 
} 

a("foo") 
# The input is "foo" 

tmplist <- list(x1 = 1, x2=2) 
a(tmplist) 
# The input is tmplist 

que funciona. Pero cuando a() se llama de otra función, ya no imprime los nombres de los argumentos originales:

b <- function(y) { 
    a(y) 
} 

b("foo") 
# The input is y 

b(tmplist) 
# The input is y 

Una solución que parece funcionar es para envolver en otro substitute y un eval:

a1 <- function(x) { 
    message("The input is ", deparse(eval(substitute(substitute(x)), parent.frame()))) 
} 

a1("foo") 
# The input is "foo" 

tmplist <- list(x1 = 1, x2=2) 
a1(tmplist) 
# The input is tmplist 

b1 <- function(y) { 
    a1(y) 
} 

b1("foo") 
# The input is "foo" 

b1(tmplist) 
# The input is tmplist 

pero esto parece poco elegante. Y falla si agrego otra capa:

c1 <- function(z) { 
    b1(z) 
} 
c1("foo") 
# The input is z 

¿Hay una buena manera general de obtener el argumento original?

+0

No soy un experto en manejo de entornos, pero creo que jugar con 'parent.frame' como lo hizo kohske, o especificar una variable global son sus únicas opciones. R no pasa por referencia como lo hace c. –

Respuesta

3

No estoy seguro de que esto va a funcionar bien en todas las situaciones, pero intente esto:

f0 <- function(x) { 
    nn <- substitute(x) 
    i <- 1 
    while(TRUE) { 
    on <- do.call("substitute", list(as.name(nn), parent.frame(i))) 
    if (on == nn) break; 
    nn <- on 
    i <- i + 1 
    } 
    message("The input is ", nn) 
} 

f1 <-function(.f1) f0(.f1) 
f2 <- function(.f2) f1(.f2) 

y luego,

> f2(foo) 
The input is foo 
> f1(poo) 
The input is poo 
> f0(moo) 
The input is moo 
> f2(";(") 
The input is ;(
> f1(":)") 
The input is :) 
> f0(":p") 
The input is :p 
+0

Eso es bastante bueno. Pero si el nombre de la variable es el mismo en dos pasos sucesivos, entonces creo que lo engaña para que se detenga antes de que realmente haya terminado. Supongamos que define f1 y f2 para que tomen 'xx' como argumento, luego se detiene allí. Tal vez la solución es mantener el bucle hasta 'i == sys.nframe()'. – wch

+0

hmm, sí ... ¿Cómo resolver ...? – kohske

0

¿Qué hay de la llamada recursiva a su función cuando

deparse(substitute(x))!=deparse(eval(substitute(substitute(x)), parent.frame()) 
1

Si bien esta es una pregunta interesante en sí misma, me pregunto si la mejor solución es solo aprobarla el nombre de la variable como un personaje, es decir, entre comillas. Entonces nada de esto es necesario. Si se necesita el objeto correspondiente al nombre, se puede obtener con get o as.name y do.call, dependiendo de cómo lo esté usando dentro de la función.

> f0 <- function(x) {message("The input is ", x)} 
> f1 <- function(.f1) f0(.f1) 
> f2 <- function(.f2) f1(.f2) 
> f2("aa") 
The input is aa 
> f1("bb") 
The input is bb 
> f0("cc") 
The input is cc 
+0

Desafortunadamente, este enfoque no funcionará en mi aplicación. Necesito pasar el objeto original. El objetivo de todo esto es simplemente imprimir un mensaje de error con los nombres de los argumentos de entrada si algo sale mal. – wch

+0

Hmmmm .... Creo que hay una manera de "propagar" el error en la cadena, de modo que un error en una función llamada activará un mensaje en la función superior, donde se conoce el "verdadero nombre" de la variable. Alguien que esté mejor educado que yo en el uso de 'error' y' trycatch' puede ser capaz de saltar aquí. ¡Una vez más, vemos la importancia de hacer la pregunta correcta! –

+1

@wch: para ampliar el último comentario de Carl, la pregunta correcta suele ser sobre lo que realmente estás tratando de hacer, no sobre cómo estás tratando de hacerlo. Es decir, no "¿Cómo uso X para hacer Y?"o peor," ¿Cómo hago X? "En lugar de eso pregunte" ¿Cómo hago Y? He intentado con X, pero no está funcionando ". – Aaron

1

Adaptación de la respuesta de kohske, aquí hay algo que funciona, pero no dejar de ir a la pila de marco prematuramente, si la variable tiene el mismo nombre en dos fotogramas sucesivos. No sé si funciona correctamente en todas las situaciones, pero parece manejar mis necesidades. La cotización de cadenas vs. variables es un poco diferente a la anterior, pero eso está bien para mi caso.

a <- function(x) { 
    newname <- substitute(x) 

    # Travel up the frame stack until we hit the top. 
    for(i in seq_len(sys.nframe())) { 
    oldname <- do.call("substitute", list(as.name(newname), parent.frame(i))) 
    newname <- oldname 
    } 
    message("The input is ", deparse(newname)) 
} 

b <- function(y) a(y) 

c <- function(z) b(z) 

a("adsf") 
# The input is adsf 
a(foo) 
# The input is foo 

b("adsf") 
# The input is adsf 
b(foo) 
# The input is foo 

c("adsf") 
# The input is adsf 
c(foo) 
# The input is foo 
Cuestiones relacionadas