简体   繁体   English

在R中,我如何定义一个等效于`deparse(substitute(x))`的函数?

[英]In R, how do I define a function which is equivalent to `deparse(substitute(x))`?

I want to write a function in R which grabs the name of a variable from the context of its caller's caller. 我想在R中编写一个函数,该函数从调用者的调用者的上下文中获取变量的名称。 I think the problem I have is best understood by asking how to compose deparse and substitute . 我认为我最好通过询问如何构成deparsesubstitute来理解这个问题。 You can see that a naive composition does not work: 您会发现幼稚的组合不起作用:

# 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

I also tried a couple of variations involving eval.parent but with no luck. 我还尝试了一些涉及eval.parent的变体,但是没有运气。 Any help is appreciated. 任何帮助表示赞赏。


Clarification : I'm not looking for a synonym for deparse(substitute(...)) , eg match.call()[[2]] - what I'm looking for is a way to define a function 澄清 :我不是在寻找deparse(substitute(...))的同义词,例如match.call()[[2]] -我正在寻找的是一种定义函数的方法

desub = function(foo) {
    ...
    # What goes here?
}

such that the definition of f above produces the same answer as g . 这样上面f的定义产生与g相同的答案。 It should look like this: 它看起来应该像这样:

> f=function(foo) { message(desub(foo)) }
> f(log)
log

Perhaps match.call could be of use in the body of desub above, but I'd like to know how. 也许match.call可能在上面的desub主体中desub ,但我想知道如何使用。 Thanks! 谢谢!

As you surmised, this is an issue with environments. 如您所料,这是环境问题。 The reason why the function f does not give log when you call f(log) , is that the environment in which substitute is called, namely the evaluation environment of desub , does not contain a binding to log . 为什么功能的原因f不给log当你调用f(log) ,是环境中substitute是所谓的,即评价环境desub ,不包含绑定log

The remedy is to evaluate the call to substitute in the proper environment, and modify desub accordingly: 补救措施是评估在适当的环境中进行substitute的调用,并相应地修改desub

desub <- function(x, env = parent.frame()) {
  deparse(eval(substitute(substitute(x)), envir = env))
}

Now f does what it was intended to do: 现在f完成预期的工作:

f(log)
#> log

Thanks to @egnha and @akrun for the brave attempts. 感谢@egnha和@akrun的勇敢尝试。 After playing around a bit I found a solution that works. 玩了一段时间后,我发现了一个可行的解决方案。

This fragment: 该片段:

desub <- function(y) {
  e1=substitute(y)
  e2=do.call(substitute,list(e1), env=parent.frame())
  deparse(e2)
}

gives: 给出:

> f <- function(x) message(desub(x))
> f(log)
log

Update: 更新:

With help from Mark Bravington on the R-devel list, I was able to generalize this to multiple frames. 在R-devel列表上的Mark Bravington的帮助下,我能够将其推广到多个框架。 I thought I should post it here, because it's a bit more useful than the above, and because there was a tricky workaround involving (possibly buggy?) behavior in parent.frame() . 我认为我应该在这里发布它,因为它比上面有用,并且因为存在一个棘手的变通办法,涉及parent.frame()行为(可能是错误的? 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"
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM