在函数外部工作但在 R 函数内部失败的参数

An argument working outside a function but fails inside a function in R

在函数外部时,下面的参数 pbkrtest.limit = nobs(m1) 工作正常(因此,不会生成任何消息)。

但是在foo()函数内部时,nobs(m1)无法识别(因此会生成一条消息)!我真的很想知道发生了什么事?

library(lme4)
library(emmeans)
dat <- read.csv('https://raw.githubusercontent.com/hkil/m/master/z.csv')

m1 <- lmer(y~ year*group + (1|stid), data = dat)

## WORKS FINE:
emtrends(m1, pairwise ~ group, var = "year", infer = c(T, T), pbkrtest.limit = nobs(m1)) 


## BUT NOW `nobs(m)` doesn't work inside the function:
foo <- function(m){

 emtrends(m, pairwise ~ group, var = "year", infer = c(T, T), pbkrtest.limit = nobs(m))
}

## RUN:
foo(m = m1)

尝试在 parent.frame 中评估它:

foo <- function(m, envir = parent.frame()) {
  s <- substitute(emtrends(m, pairwise ~ group, var = "year", infer = c(TRUE, TRUE), 
    pbkrtest.limit = nobs(m)))
  eval(s, envir)
}

foo(m = m1)

并通过 https://github.com/rvlenth/emmeans/issues

向开发人员报告

也使用 TRUE 而不是 T,因为 T 可以被覆盖但 TRUE 不能。

我根据 emtrends() 中的实际代码更仔细地研究了这一点。它归结为以下代码中演示的内容:

# comparison of evaluation methods with lazy eval

# a trivial function
foo = function(x, ...)
    x^2


# calls foo three ways
foodoo = function(...) {
    message("x1 = ", foo(...))
    message("x2 = ", do.call("foo", list(...)))
    cl = match.call()
    cl[[1]] = quote(foo)
    message("x3 = ", eval(cl))
}

# test value
val = 3.5

# test runs
foodoo(x = 5)
## x1 = 25
## x2 = 25
## x3 = 25

foodoo(x = val)
## x1 = 12.25
## x2 = 12.25
## x3 = 12.25

# same tests, wrapped in a function...
bar = function(z) {
    foodoo(x = z)
}

bar(5)
## x1 = 25
## x2 = 25
## Error in foo(x = z): object 'z' not found

bar(val)
## x1 = 12.25
## x2 = 12.25
## Error in foo(x = z): object 'z' not found

reprex package (v0.3.0)

于 2020-05-31 创建

OP 遇到的问题与第三种评估方法有关。在 emtrends() 中,代码匹配调用,将其修复以供不同的函数使用,然后对其求值。我不确定我是否完全理解所有这些,但是函数调用中传递的实际上是承诺,而不是值;一个 promose 由一个未计算的表达式和它应该被计算的环境组成。

在查看 foodoo() 时,似乎在使用前两种方法时,这些承诺会在调用 foo 之前更新到当前环境,而在第三种方法中,原始调用保持完好无损,这样承诺就不会改变。显然(我仍然不太清楚为什么),当我们从全局环境调用 foodoo() 时,那些原始环境已经足够好了,但从不同的环境调用时显然不够好。如果有人愿意发表评论和进一步解释,我洗耳恭听。

无论如何,我很高兴,因为我能够通过强制评估所有 ... 参数并将它们替换为 cl:

来解决问题
# new foodoo
foodoo = function(...) {
    message("x1 = ", foo(...))
    message("x2 = ", do.call("foo", list(...)))
    cl = match.call()
    cl[[1]] = quote(foo)
    dots = eval(list(...))      # added
    cl[names(dots)] = dots      # added
    message("x3 = ", eval(cl))
}

bar(5)
## x1 = 25
## x2 = 25
## x3 = 25

bar(val)
## x1 = 12.25
## x2 = 12.25
## x3 = 12.25