【问题标题】:apply() and forceAndCall() ignoring get() from parent.frame()apply() 和 forceAndCall() 忽略来自 parent.frame() 的 get()
【发布时间】:2019-08-27 10:34:27
【问题描述】:

我不明白为什么这段代码在最后一行打印失败:

f <- function(x) get('v', envir = parent.frame(), inherits = TRUE)

run <- function() {

  v <- 'test variable'

  print(f())
  print((function() f())())
  print(apply(X = data.frame(1:2), MARGIN = 1, f))
}
run()

与:

 Error in get("v", envir = parent.frame(), inherits = TRUE) : 
  object 'v' not found 

第一个打印语句显示vparent.frame(1) 中找到。第二个打印语句显示由于inherits = TRUE,在parent.frame(2) 中找到了v。最后一个是谜。

似乎apply 忽略了get(envir = parent.frame())。我将其追溯到apply() 中的函数forceAndCall()。我错过了什么还是这是一个错误?

在实际应用中,v只定义在函数f的调用环境中(即在parent.frame()或以上),而从不在parent.env()中。

【问题讨论】:

  • 有趣。我在第一行print 之后添加了一个包装f 的函数g &lt;- function() { f() }; print(g()),它按预期执行并且没有错误。所以inherits 参数按预期工作......

标签: r apply


【解决方案1】:

不转发解决方案,但可以避免错误。 parent.frame 的手册说:sys.frame(sys.parent(n)) 的方便简写。使用 sys.frame(sys.parent(n)) 会导致相同的错误。但是,当我要求sys.parents() 并取最后一个之前的错误时,错误就会消失,或者只是使用@DavorJosipovic 发现的dynGet

#f <- function(x) get('v', envir = sys.frame(max(0, sys.parents()[length(sys.parents())-1])), inherits = TRUE)
f <- function(x) dynGet('v', inherits = TRUE)
run <- function() {
  v <- 'test variable'
  print(f())
  print((function() f())())
  print(apply(X = data.frame(1:2), MARGIN = 1, f))
}
run()
#[1] "test variable"
#[1] "test variable"
#[1] "test variable" "test variable"

【讨论】:

  • 它很hackish,但它可以工作@GKi:D。在实际应用程序中,我通过参数传递解决了它。到f。但你说得有道理。 sys.parents() 返回正确的索引。真正的解决方案——据我到目前为止的理解——将涉及在每个sys.frame() 环境中为每个sys.parents() 检查v。似乎getinherits = TRUE 检查了parent.frame() 的父environments,将其引导到另一个不是调用堆栈的继承树。所以我越想它,我就越觉得这是一个不幸的功能,而不是一个真正的错误:/.
  • 现在我意识到了上述情况:似乎dynGet('v', inherits = TRUE) 正是这样做的。 dynGet() 有点实验性,可以在另一个函数中使用。它在调用者中查找对象,即函数的 sys.frame()s。谨慎使用。好吧,GKi,因为我还不能回答我自己的问题:你很荣幸:D。
  • @DavorJosipovic 哦,是的!只需f &lt;- function(x) dynGet('v', inherits = TRUE) 即可。
【解决方案2】:

这里的关键问题是调用环境和get(inherits=TRUE) 处理它们的方式。对于初学者,我建议阅读 Hadley 的 Function environments

更具体地说,必须了解函数的封闭和调用环境才能了解发生了什么。

v是在函数f调用环境中定义的——从函数的执行环境来看——parent.frame(1L)在第一个print语句中,parent.frame(2L)在最后两个print语句中。

我认为如果我为envir=parent.frame(1L) 提供inherits=TRUEget 函数将遍历parent.frame() 层次结构并最终在父调用 环境之一中找到v。它不是。实际上,它从envir 环境开始提供并在父封闭 环境中查找v。对于第二个 print 语句,parent.frame(1L) 的封闭父环境正是定义 v 的位置,但在最后一个 print 语句中,parent.frame(1L) 的封闭父环境是定义 applybase 命名空间.在此之上封闭父环境是全局环境。所以v 没有找到。

我们真正需要的是一个get 函数,它不会在父封闭环境层次结构中搜索v,而是在父调用环境层次结构中搜索。这正是dynget() 所做的。

dynGet() 有点实验性,可以在另一个内部使用 功能。它在调用者中寻找一个对象,即 sys.frame()s 函数。谨慎使用

此函数将遍历父调用环境sys.parents()sys.frame(),并在每个调用环境中查找v。所以f &lt;- function(x) dynGet('v', inherits = TRUE) 解决了这个问题。

【讨论】:

    【解决方案3】:

    正如@Davor 所说,调用apply 添加了另一层 parent.frame() 导致新的环境路径,这是在两个不同环境中搜索的一种方法。

    f <- function(x) {
      cat('f Env.')
      print(environment())
      #browser()
      if(is.null(get0('v', parent.frame()))) {
        get('v', envir = parent.frame(2), inherits = TRUE)} else {
          get('v', envir = parent.frame(1), inherits = TRUE)}
    }
    
    run <- function() {
      cat('run Env.')
      print(environment())
      v <- 'test variable'
      print(f())
      print((function() f())())
      print(apply(X = data.frame(1:2), MARGIN = 1, f))
    
    }
    run()
    

    如果我们在f 中取消注释browser(),在run 中注释第一个和第二个print 并重新运行代码,我们可以看到parent.frame()parent.frame(2) 有不同的parent.env

    【讨论】:

    • 我想我有。当从 apply 中调用时,parent.frame() 解析为 &lt;environment: namespace:base&gt; 下的 &lt;environment: 0x00000000211164a8&gt;。上面是全局环境,所以它永远找不到vparent.frame(2) 正确找到了定义 v 的环境。所以就像apply 正在改变调用堆栈。
    • 是的,旧的run() 的表述并不理想。我觉得现在好多了。无论如何,parent.frame(2) 不是真正的解决方案。 get('v', envir = parent.frame(), inherits = TRUE) 在任何情况下都应该有效,但由于我之前的评论,它没有。我不确定如何理解这一点:错误还是功能?文档说 严格来说, sys.parent 和 parent.frame 指的是父解释函数的上下文。所以内部函数(可能会或可能不会设置上下文,因此可能会或可能不会出现在调用堆栈上)可能不会被计算在内,S3 方法也可以做一些令人惊讶的事情。 是的...
    • "仅适用于n=2 print(apply(X = data.frame(1:2), MARGIN = 1, f)) 有效。" print((function() f())()) 由于额外调用而应该也有效。我了解堆栈增加,但在此示例中,发生了其他事情:print(apply(X = data.frame(1:2), MARGIN = 1, f)) 更改调用层次结构,而print((function() f())()) 维护它。这就是为什么print((function() f())()) 也适用于n = 1inheritance = TRUEapply 引入的这种不断变化的调用层次结构是我不明白的。
    【解决方案4】:

    我不确定这是否完全回答了这个问题,我也不完全理解命名空间/环境是如何解决的。但是请考虑对您的代码进行此扩展,我在其中定义了自己的“应用”函数。一个在调用run函数中定义,一个在外部定义:

    X <- data.frame(1:2)
    
    my_apply1 <- function(X, FUN) {
        for (i in seq_along(X)) {
          print(FUN(X[[i]]))
        }
    }
    run <- function() {
      f <- function(x) get('v', envir = parent.frame(), inherits = TRUE)
    
      v <- 'test variable'
    
      print(f())
      g <- function() { f() };   print(g())
    
      my_apply2 <- function(X, FUN) {
        for (i in seq_along(X)) {
          print(FUN(X[[i]]))
        }
      }
    
      my_apply2(X, f)
      my_apply1(X, f)
    
    
      #print(my_apply(matrix(1:6, ncol=2), MARGIN = 1, f))
    }
    run()
    
    [1] "test variable"
    [1] "test variable"
    [1] "test variable"
    Error in get("v", envir = parent.frame(), inherits = TRUE) :
        object 'v' not found
    

    这向我表明,环境继承与嵌套函数的路径不同,因为解析发生在

    run() => f() => base::apply() => f() => where is v?
    

    而找到变量v 则返回定义函数的位置

    where is v? => f() => base::apply => base => v?
    

    【讨论】:

    • 所以,如果我没听错的话,这意味着inherits = TRUE 令人困惑,因为它不适用于envir 中定义的环境,但始终适用于parent.env() 及更高版本。
    • 你可能会在那里找到一些东西,它实际上是在追随 apply 的封闭环境,而不是 f (这是我在回答中得出的),但你的评论很明确更好。
    • 我刚刚更新了问题。 inherits = TRUE 很好地遵循了 envir 参数。所以问题出在其他地方。参看。苏利曼的回答。
    猜你喜欢
    • 1970-01-01
    • 2023-03-17
    • 2023-02-17
    • 2020-08-11
    • 1970-01-01
    • 2019-05-26
    • 2021-05-02
    • 1970-01-01
    • 2012-01-03
    相关资源
    最近更新 更多