eval
vs get
I’ve been fighting damn scoping issues lately and finally sat down to figure out what works and when. Documenting some of them here
The lessons I’ve learned from this:
eval(a, envir = ... )
DOES NOT WORK!eval(str2lang("a"), envir = ...)
eval.parent(a)
DOES NOT WORK!eval.parent(str2lang("a"))
works and SKIPS GENERIC!get("a", envir = ...)
seems to always work as expected
So to summarize, I think get
is always a safer option than
eval
. In addition, get
is much faster:
> f1 <- function() {
+ eval.parent(str2lang("a"))
+ }
> f2 <- function() {
+ get("a", envir = sys.frame(0))
+ }
> a <- 1
> microbenchmark:::microbenchmark(f1(), f2(), times = 10000)
Unit: nanoseconds
expr min lq mean median uq max neval
f1() 1353 1558 1854.5325 1640 1763 1019547 10000
f2() 533 656 808.4462 697 738 800074 10000
Obviously this doesn’t cover more advanced situations, like evaluating full calls, but I’m still working on puzzling all that out.
Here’s the sample code I used to evaluate this:
foo <- function(x) {
a <- "in generic"
UseMethod("foo")
}
foo.numeric <- function(x) {
a <- "in specific"
print(sys.calls())
# Itself
print(paste("a:",
a
))
# Eval
print(paste("eval(a):",
eval(a)
))
print(paste('eval(str2lang("a")):',
eval(str2lang("a"))
))
# eval.parent
print(paste("eval.parent(a):",
eval.parent(a)
))
print(paste('eval.parent(str2lang("a")):',
eval.parent(str2lang("a"))
))
# eval with sys.frames
print(paste("eval(a, envir = sys.frame(0)):",
eval(a, envir = sys.frame(0))
))
print(paste('eval(str2lang("a"), envir = sys.frame(0)):',
eval(str2lang("a"), envir = sys.frame(0))
))
print(paste("eval(a, envir = sys.frame(1)):",
eval(a, envir = sys.frame(1))
))
print(paste('eval(str2lang("a"), envir = sys.frame(1)):',
eval(str2lang("a"), envir = sys.frame(1))
))
# get
print(paste('get("a"):',
get("a")
))
# get with sys.frames
print(paste('get("a", envir = sys.frame(0)):',
get("a", envir = sys.frame(0))
))
print(paste('get("a", envir = sys.frame(1)):',
get("a", envir = sys.frame(1))
))
}
a <- "in global"
foo(1)
[[1]]
foo(1)
[[2]]
UseMethod("foo")
[1] "a: in specific"
[1] "eval(a): in specific"
[1] "eval(str2lang(\"a\")): in specific"
[1] "eval.parent(a): in specific"
[1] "eval.parent(str2lang(\"a\")): in global"
[1] "eval(a, envir = sys.frame(0)): in specific"
[1] "eval(str2lang(\"a\"), envir = sys.frame(0)): in global"
[1] "eval(a, envir = sys.frame(1)): in specific"
[1] "eval(str2lang(\"a\"), envir = sys.frame(1)): in generic"
[1] "get(\"a\"): in specific"
[1] "get(\"a\", envir = sys.frame(0)): in global"
[1] "get(\"a\", envir = sys.frame(1)): in generic"
Home |
Back to blog
This work is licensed under CC BY-NC 4.0