eval: why does enclos = parent.frame() make a difference?

大憨熊 提交于 2020-01-16 03:51:08

问题


In this question, the following throws an error:

subset2 = function(df, condition) {
  condition_call = eval(substitute(condition),df )  
  df[condition_call,]
}

df = data.frame(a = 1:10, b = 2:11)
condition = 3

subset2(df, a < condition)
## Error in eval(substitute(condition), df) : object 'a' not found

Josh and Jason from the original question did a great job explaining why this is. What I don't get is why supplying the enclos argument to eval apparently fixes it.

subset3 = function(df, condition) {
  condition_call = eval(substitute(condition), envir = df, enclos = parent.frame())
  df[condition_call, ]
}

subset3(df, a < condition)
##   a b
## 1 1 2
## 2 2 3

I understand that skipping the function environment means R is no longer trying to evaluate the promise, and instead grabs the condition object from the global environment.

But I think supplying enclos = parent.frame() should not make a difference. From ?eval on the enclos argument:

Specifies the enclosure, i.e., where R looks for objects not found in envir.

But if not provided, it defaults to:

enclos = if(is.list(envir) || is.pairlist(envir)) parent.frame() else baseenv())

which, in my mind, should resolve to parent.frame() anyway, because surely, df satisfies the is.list() check.

This means that as long as some object data returns TRUE on is.list(), the behavior of eval(expr, envir = data) and eval(expr, envir = data, enclos = parent.frame()) should be identical. But as evidenced by the above, it isn't.

What am I missing?

EDIT: Thanks to SmokeyShakers who pointed out the difference between default and user-supplied arguments regarding the time of evaluation. I think this is actually already expressed here: https://stackoverflow.com/a/15505111/2416535

It might make sense to keep this one alive though, as it touches eval() specifically (the other does not), and it is not trivial to realize what the generalized question should be until one has the answer.


回答1:


So, different parents. In the example that doesn't work parent.frame is looking up from inside eval into the internal environment of subset2. In the working example, parent.frame is looking up from inside subset3, likely to your global where your df sits.

Example:

tester <- function() {
  print(parent.frame())
} 

tester() # Global

(function() {
  tester()
})() # Anonymous function's internal env



回答2:


IT is getting confused by the double use of condition. Change one of them to cond:

cond <- 3
subset2(df, a < cond)
##   a b
## 1 1 2
## 2 2 3

Note that even though this works it won't work if you put subset2 into a function since it will look into the global environment where subset2 was defined rather than looking inside the f execution frame and subset3 will be needed.

if (exists("cond")) rm(cond)
f <- function() {
  cond <- 3
  subset2(df, a < cond)
}
f() # error


来源:https://stackoverflow.com/questions/59584695/eval-why-does-enclos-parent-frame-make-a-difference

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!