Reputation: 4117
Consider this function a()
, which prints out the argument that was passed in:
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
That works. But when a()
is called from another function, it no longer prints out the original argument names:
b <- function(y) {
a(y)
}
b("foo")
# The input is y
b(tmplist)
# The input is y
One solution that seems to work is to wrap in another substitute
and an 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
But this seems inelegant. And it fails if I add another layer:
c1 <- function(z) {
b1(z)
}
c1("foo")
# The input is z
Is there a good, general way to get the original argument?
Upvotes: 6
Views: 924
Reputation: 4117
Adapting kohske's answer, here is something that works, but doesn't stop going up the frame stack prematurely, if the variable has the same name in two successive frames. I don't know if it work correctly in all situations, but it seems to handle my needs. The quoting of strings vs. variables is a little different from before, but that's OK for my case.
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
Upvotes: 3
Reputation: 7475
How about recursively calling your function when
deparse(substitute(x))!=deparse(eval(substitute(substitute(x)), parent.frame())
Upvotes: 0
Reputation: 37764
While this is an interesting question in its own right, I wonder if the best solution is just to pass the variable name as a character, ie, in quotes. Then none of this is necessary. If the object corresponding to the name is needed, it can then be gotten with get
or as.name
and do.call
, depending on how you're using it within the function.
> 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
Upvotes: 1
Reputation: 66852
I'm not sure it this will work well in all situations, but try this:
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)
and then,
> 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
Upvotes: 3