2012-06-05 9 views
6

Betrachten Sie diese Funktion a(), die das Argument ausdruckt, die in übergeben wurde:Mit Ersatz() Argument Namen zu bekommen, mehrere Ebenen bis

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 

, das funktioniert. Aber wenn a() aus einer anderen Funktion aufgerufen wird, ist es nicht mehr druckt die ursprünglichen Argumentnamen aus:

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

b("foo") 
# The input is y 

b(tmplist) 
# The input is y 

Eine Lösung, die zu funktionieren scheint in einer anderen substitute zu wickeln und ein 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 

Aber das scheint unelegant. Und es schlägt fehl, wenn ich einen weiteren Layer hinzufüge:

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

Gibt es eine gute, allgemeine Möglichkeit, das ursprüngliche Argument zu erhalten?

+0

Ich bin kein Experte in der Handhabung von Umgebungen, aber ich denke, entweder mit 'parent.frame' zu ​​spielen, wie es bei kohske der Fall war, oder eine globale Variable anzugeben, sind Ihre einzigen Entscheidungen. R geht nicht wie bei c durch Referenz. –

Antwort

3

Ich bin mir nicht sicher, dass es diese in allen Situationen gut funktionieren wird, aber versuchen Sie dies:

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) 

und dann

> 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

Das ist ziemlich gut. Aber wenn der Name der Variablen in zwei aufeinanderfolgenden Schritten derselbe ist, dann denke ich, dass es ihn zum Stoppen bringt, bevor er wirklich fertig ist. Angenommen, Sie definieren f1 und f2 so, dass sie 'xx' als Argument verwenden - dann hört es dort auf. Vielleicht besteht die Lösung darin, bis "i == sys.nframe()" zu loopen. – wch

+0

hmm, ja ... Wie löst man ...? – kohske

0

Wie wäre Ihre Funktion rekursiv aufrufen, wenn

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

Während dies eine interessante Frage an sich ist, frage ich mich, ob die beste Lösung nur besteht, um zu bestehen der Variablenname als ein Zeichen, dh in Anführungszeichen. Dann ist nichts davon notwendig. Wenn das Objekt, das dem Namen entspricht, benötigt wird, kann es mit get oder as.name und do.call abgerufen werden, je nachdem, wie Sie es innerhalb der Funktion verwenden.

> 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

Leider funktioniert dieser Ansatz in meiner Anwendung nicht. Ich muss das Originalobjekt weitergeben. Der Zweck all dies ist, nur eine Fehlermeldung mit den Namen der Eingabeargumente auszudrucken, wenn etwas schief geht. – wch

+0

Hmmmm .... Ich glaube, es gibt eine Möglichkeit, den Fehler in der Kette zu "propogieren", so dass ein Fehler in einer aufgerufenen Funktion eine Nachricht in der oberen Funktion auslöst, wo Sie den "wahren Namen" der Variablen kennen. Jemand, der besser ausgebildet ist als ich in der Verwendung von 'error' und' trycatch', kann hier vielleicht einspringen. Wieder einmal sehen wir die Wichtigkeit, die richtige Frage zu stellen! –

+1

@wch: Um Carls letzten Kommentar zu erweitern, ist die richtige Frage in der Regel, was Sie eigentlich versuchen, nicht darüber, wie Sie es versuchen. Das heißt nicht "Wie verwende ich X um Y zu machen?"oder schlechter," Wie mache ich X? "Stattdessen frage" Wie mache ich Y? Ich habe X ausprobiert, aber es funktioniert nicht. " – Aaron

1

Anpassung kohske Antwort, hier ist etwas, das funktioniert, aber nicht stoppen den Rahmen Stapel vorzeitig steigen, wenn die Variable den gleichen Namen in zwei aufeinanderfolgenden Rahmen hat. Ich weiß nicht, ob es in allen Situationen richtig funktioniert, aber es scheint meine Bedürfnisse zu erfüllen. Das Zitieren von Strings vs. Variablen ist etwas anders als vorher, aber das ist in Ordnung für meinen Fall.