【问题标题】:Purpose of this R idiom (match.call followed by eval(parent.frame())这个 R 成语的目的(match.call 后跟 eval(parent.frame())
【发布时间】:2019-04-05 12:11:11
【问题描述】:

我继承了一些代码,这些代码使用了我认为是库的常见 R 习惯用法,但我不确定以这种冗长的方式编写会实现什么。最终我打算重写,但我想先知道为什么,然后再做一些愚蠢的事情。

ecd <-
function(formula, data, subset, weights, offset, ...) {
    cl = match.call()
    mf = match.call(expand.dots = FALSE)
    m =
        match(c("formula", "data", "subset", "weights", "offset"),
              names(mf),
              0L)
    mf = mf[c(1L, m)]
    mf$drop.unused.levels = TRUE
    mf[[1L]] = quote(stats::model.frame)
    mf = eval(mf, parent.frame())
    mt = attr(mf, "terms")
    y = stats::model.response(mf, "numeric")
    w = as.vector(stats::model.weights(mf))

    offset = as.vector(stats::model.offset(mf))

    x = stats::model.matrix(mt, mf, contrasts)
    z = ecd.fit(x, y, w, offset, ...)

我目前的理解是它从函数的原始参数构造一个函数调用对象(类型?)然后手动调用它,而不是直接调用stats::model.frame。任何见解将不胜感激。

【问题讨论】:

  • eval(mf,parent.frame()) 正在从合适的环境中获取mf
  • 我以为是在父环境中调用mf,不是吗?
  • eval evaluates the expr argument in the environment specified by envir and returns the computed value. If envir is not specified, then the default is parent.frame() (the environment where the call to eval was made). 来自文档 (help("eval"))。
  • 是的,所以它在父环境中评估 mf,它以通常的方式获取mf(作为封闭框架的函数参数),然后返回在父框架中评估mf 的值(然后将其分配给一个名为mf 的变量......好吧,我没有写这个)。当然,除非“获取mf”是指分配变量(“新”mf),在这种情况下我认为我们是一致的。

标签: r


【解决方案1】:

我认为这应该可以回答所有问题,解释在代码中:

# for later
FOO <- function(x) 1000 * x
y <- 1

foo <- function(...) {
  cl = match.call()
  message("cl")
  print(cl)
  message("as.list(cl)")
  print(as.list(cl))
  message("class(cl)")
  print(class(cl))
  # we can modify the call is if it were a list
  cl[[1]] <- quote(FOO)
  message("modified call")
  print(cl)
  y <- 2
  # now I want to call it,  if I call it here or in the parent.frame might
  # give a different output
  message("evaluate it locally")
  print(eval(cl))
  message("evaluate it in the parent environment")
  print(eval(cl, parent.frame()))
  message("eval.parent is equivalent and more idiomatic")
  print(eval.parent(cl))
  invisible(NULL)
}
foo(y)

# cl
# foo(y)
# as.list(cl)
# [[1]]
# foo
# 
# [[2]]
# y
# 
# class(cl)
# [1] "call"
# modified call
# FOO(y)
# evaluate it locally
# [1] 2000
# evaluate it in the parent environment
# [1] 1000
# eval.parent is equivalent and more idiomatic
# [1] 1000

【讨论】:

  • 感谢您对代码的解释,我也很想了解您为什么要这样做?据我所知,它使代码更难阅读和理解,并可能使您受到函数外部的奇怪影响?我听到的关于这种 NSE 的解释只是为了获取绘图标签的变量名。
  • 我不认为代码很干净,没有注释,mf 不是同一种对象,具体取决于您查看的位置并且有一个神秘的名称。直接在函数中调用stats::model.frame 而不进行替换可能更清楚。
  • match.call() 的第一个元素上的替换具有这样的优势,即您明确地采用与原始调用完全相同的参数。使用stats::model.frame(formula, data, subset, weights, offset, ...),您输入了两次这些变量,因此如果发生变化,您必须在这两个位置进行更新,并且下一位读者必须进行视觉比较,以注意您传递的参数与原始调用中的参数相同。跨度>
  • 此外,如果函数支持缺少参数,您将遇到问题(例如,如果缺少subset,上述调用将失败),这可以通过足够的if (missing(subset)){...} 等处理,但它更多详细。
  • 谢谢,我认为这可以解决所有问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-06
  • 1970-01-01
  • 1970-01-01
  • 2015-01-11
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多