在 R 中,如何定义等同于 `deparse(substitute(x))` 的函数?
In R, how do I define a function which is equivalent to `deparse(substitute(x))`?
我想在 R 中编写一个函数,它从调用者的调用者的上下文中获取变量的名称。我认为我遇到的问题最好通过询问如何组合 deparse
和 substitute
来理解。可以看到天真的作文是行不通的:
# a compose operator
> `%c%` = function(x,y)function(...)x(y(...))
# a naive attempt to combine deparse and substitute
> desub = deparse %c% substitute
> f=function(foo) { message(desub(foo)) }
> f(log)
foo
# this is how it is supposed to work
> g=function(foo) { message(deparse(substitute(foo))) }
> g(log)
log
我还尝试了一些涉及 eval.parent
的变体,但没有成功。感谢您的帮助。
澄清:我不是在寻找 deparse(substitute(...))
的同义词,例如match.call()[[2]]
- 我正在寻找一种定义函数的方法
desub = function(foo) {
...
# What goes here?
}
使得上面 f
的定义产生与 g
相同的答案。它应该是这样的:
> f=function(foo) { message(desub(foo)) }
> f(log)
log
也许 match.call
可以用在上面 desub
的正文中,但我想知道如何使用。谢谢!
如您所料,这是环境问题。函数f
之所以在调用f(log)
时没有给出log
,是因为调用substitute
的环境,即[=16=的求值环境], 不包含对 log
.
的绑定
补救措施是在适当的环境中评估对substitute
的调用,并相应地修改desub
:
desub <- function(x, env = parent.frame()) {
deparse(eval(substitute(substitute(x)), envir = env))
}
现在 f
完成了预期的工作:
f(log)
#> log
感谢@egnha 和@akrun 的勇敢尝试。经过一番尝试后,我找到了一个可行的解决方案。
这个片段:
desub <- function(y) {
e1=substitute(y)
e2=do.call(substitute,list(e1), env=parent.frame())
deparse(e2)
}
给出:
> f <- function(x) message(desub(x))
> f(log)
log
更新:
在 R-devel 列表上的 Mark Bravington 的帮助下,我能够将其推广到多个帧。我想我应该 post 它在这里,因为它比上面的更有用,而且因为有一个棘手的解决方法涉及 parent.frame()
.
中的行为(可能是错误的?)
# desub(v,0)=="v"
# desub(v,1)==deparse(substitute(v))
# desub(v,2)==name of v in grandparent's frame
# etc.
desub = function(y,n=1) {
env=environment();
for(i in 0:n) {
y = do.call(substitute, list(substitute(y)), env=env)
env = do.call(my_mvb_parent, list(), env=env)
}
deparse(y)
}
# helper:
#
# - using mvb.parent.frame fixes problems with capture.output and
# weird cycling behavior in the built-in parent.frame
#
# - this wrapper makes mvb.parent.frame not throw an error when we get
# to globalenv()
my_mvb_parent=function() {
library(mvbutils)
tryCatch(
mvb.parent.frame(2),
error=function(e) { globalenv()})
}
if(1) {
# example code
g2=function(t) {
for(i in 0:5) {
res=desub(t,i);
print(res);
res1=capture.output(desub(t,i))
stopifnot(capture.output(res)==res1)
}
}
g1=function(z) g2(z)
g=function(y) g1(y)
g(log)
# prints:
## [1] "t"
## [1] "z"
## [1] "y"
## [1] "log"
## [1] "log"
## [1] "log"
}
我想在 R 中编写一个函数,它从调用者的调用者的上下文中获取变量的名称。我认为我遇到的问题最好通过询问如何组合 deparse
和 substitute
来理解。可以看到天真的作文是行不通的:
# a compose operator
> `%c%` = function(x,y)function(...)x(y(...))
# a naive attempt to combine deparse and substitute
> desub = deparse %c% substitute
> f=function(foo) { message(desub(foo)) }
> f(log)
foo
# this is how it is supposed to work
> g=function(foo) { message(deparse(substitute(foo))) }
> g(log)
log
我还尝试了一些涉及 eval.parent
的变体,但没有成功。感谢您的帮助。
澄清:我不是在寻找 deparse(substitute(...))
的同义词,例如match.call()[[2]]
- 我正在寻找一种定义函数的方法
desub = function(foo) {
...
# What goes here?
}
使得上面 f
的定义产生与 g
相同的答案。它应该是这样的:
> f=function(foo) { message(desub(foo)) }
> f(log)
log
也许 match.call
可以用在上面 desub
的正文中,但我想知道如何使用。谢谢!
如您所料,这是环境问题。函数f
之所以在调用f(log)
时没有给出log
,是因为调用substitute
的环境,即[=16=的求值环境], 不包含对 log
.
补救措施是在适当的环境中评估对substitute
的调用,并相应地修改desub
:
desub <- function(x, env = parent.frame()) {
deparse(eval(substitute(substitute(x)), envir = env))
}
现在 f
完成了预期的工作:
f(log)
#> log
感谢@egnha 和@akrun 的勇敢尝试。经过一番尝试后,我找到了一个可行的解决方案。
这个片段:
desub <- function(y) {
e1=substitute(y)
e2=do.call(substitute,list(e1), env=parent.frame())
deparse(e2)
}
给出:
> f <- function(x) message(desub(x))
> f(log)
log
更新:
在 R-devel 列表上的 Mark Bravington 的帮助下,我能够将其推广到多个帧。我想我应该 post 它在这里,因为它比上面的更有用,而且因为有一个棘手的解决方法涉及 parent.frame()
.
# desub(v,0)=="v"
# desub(v,1)==deparse(substitute(v))
# desub(v,2)==name of v in grandparent's frame
# etc.
desub = function(y,n=1) {
env=environment();
for(i in 0:n) {
y = do.call(substitute, list(substitute(y)), env=env)
env = do.call(my_mvb_parent, list(), env=env)
}
deparse(y)
}
# helper:
#
# - using mvb.parent.frame fixes problems with capture.output and
# weird cycling behavior in the built-in parent.frame
#
# - this wrapper makes mvb.parent.frame not throw an error when we get
# to globalenv()
my_mvb_parent=function() {
library(mvbutils)
tryCatch(
mvb.parent.frame(2),
error=function(e) { globalenv()})
}
if(1) {
# example code
g2=function(t) {
for(i in 0:5) {
res=desub(t,i);
print(res);
res1=capture.output(desub(t,i))
stopifnot(capture.output(res)==res1)
}
}
g1=function(z) g2(z)
g=function(y) g1(y)
g(log)
# prints:
## [1] "t"
## [1] "z"
## [1] "y"
## [1] "log"
## [1] "log"
## [1] "log"
}