R - 如何在被调用函数内找到调用函数的环境?

时间:2015-07-21 01:48:11

标签: r scope

我正在尝试创建一个函数,该函数引用调用它的范围来创建唯一ID:

uniqueid <- function(name, envir = NULL) {

  if(identical(envir, globalenv())) {
    e = envir
  } else if (is.null(envir)) {
    e = parent.frame()
  } else if (is.environment(envir)) {
    e = envir
  } else {
    stop("given non-default argument \'envir\' is not an environment")
  }

  return(paste(sep = "",
    sub('<environment: (.*)>', '\\1', capture.output(e)),
    ".",
    name
    )
  )
}

我能做些什么才能让这项工作按照我认为应该有效的方式进行? R不断返回定义函数的范围,而不是返回它的评估位置:

hello <- new.env()
hello$env <- new.env()

eval(envir=hello$env, {
  print(environment())
  print(hello$env) #hello$env is an environment
  print(hello) #hello is an environment
  uniqueid('hi')
})

我试图让其中至少有一个匹配起来,但它似乎并不想工作。 R要么返回全局环境,要么为为函数调用的实例创建的临时帧/环境的不断变化的范围。 ID需要在多次调用之间重现,并且取决于环境。

我知道我可以通过环境,但我开始怀疑是否真的可以捕获调用者的环境。

2 个答案:

答案 0 :(得分:3)

TL,DR:parent.frame()不是parent.frame(environment()); evalq不是eval

parent.frame将参数n作为要返回的世代数,而不是environment()调用的结果(这是一个环境)。如果您将其排除在parent.frame()电话之外,那就很好。

此外,yoru示例代码并不像您期望的那样工作,因为在environment()的调用中正在评估您的eval命令,然后才将作为的一部分进行评估 eval。即你必须引用你的论点eval。或者,使用evalq而不是eval

E.g。

uniqueid <- function(name) {
  print(parent.frame())
}
hello <- new.env()
hello$env <- new.env()
evalq(envir=hello$env, {
  print(environment()) # prints hello$env
  print(hello$env)     # prints hello$env
  print(hello)         # prints hello
  uniqueid('hi')       # prints hello$env
})

E.g。这将获取环境ID并将其添加到name

uniqueid <- function(name) {
    # GlobalEnv etc have names; hello doesn't
    envName <- environmentName(parent.frame())
    # get the environment ID from the parent.frame() output if no name
    if (envName == "") {
        p <- parent.frame()
        envName <- sub('<environment: (.*)>', '\\1', capture.output(p))
    }
    return(paste(envName, name, sep=':'))
}
uniqueid('x') # R_GlobalEnv:x
print(hello) # <environment: 0x4641fa0>
evalq(envir=hello, uniqueid('x')) # "0x4641fa0:x"
print(hello$env) # <environment: 0x4640e60>
evalq(envir=hello$env, uniqueid('x')) # "0x4640e60:x"

答案 1 :(得分:2)

您的问题是eval()期望某些内容未被评估。通过传入代码块{},您无法阻止评估。例如,考虑更简单的情况

a<-10
ee<-new.env()
ee$a<-5

eval(envir=ee, a)
# [1] 10
eval(envir=ee, {a})
# [1] 10
eval(envir=ee, quote(a))
# [1] 5
evalq(envir=ee, a)
# [1] 5

注意最后两个如何显式引用表达式,或使用evalq()来引用表达式。这些允许评估延迟,直到在请求的环境中执行。

因此,如果您只是将eval()更改为evalq(),那么您应该没问题。