In Hadley's Advanced R book, there is a piece of code that I cannot understand the output.
f <- function(x) substitute(x)
g <- function(x) deparse(f(x))
g(1:10)
g(x)
g(x + y ^ 2 / z + exp(a * sin(b)))
Why do they all return "x"
? Especially when
g <- function(x) deparse(substitute(x))
returns the "1:10"
, "x"
, and "x + y ^ 2 / z + exp(a * sin(b))"
as expected.
Non-standard evaluation shows you how subset() works by combining substitute() with eval() to allow you to succinctly select rows from a data frame. Scoping issues discusses scoping issues specific to NSE, and will show you how to resolve them.
Metaprogramming. The final use of non-standard evaluation is to do metaprogramming. This is a catch-all term that encompasses any function that does computation on an unevaluated expression.
Lazy evaluation also known as call by need is a technique where the expression's evaluation is delayed until it's value is absolutely needed. In other words, it's used to avoid repeated evluations. Lazy evaluation is used in R as it increases the efficiency of the program when used interatively.
First, some background information: A promise is an unevaluated argument. A promises comprises of two parts: 1) the code / expression that gives rise to this delayed computation (this code can be viewed by substitute
or pryr::promise_info
), and 2) the environment where this code / expression is created and should be evaluated in (this environment can be viewed by pryr::promise_info
).
The question is also clearer if you changed the g()
function to
g <- function(x) deparse(f(whatever))
you would always get "whatever". This is because when g()
calls f(whatever)
, it passes a promise object to f()
--this object has the code whatever
and the environment of g()
's execution environment. Then, the substitute
within f()
looks at this promise object and returns the code / expression of that promise, which is whatever
in this case.
The code and the environment of the promise object can be confirmed by running the following code:
library(pryr) # need to install it
f <- function(x) {
print(promise_info(x))
substitute(x)
}
g <- function(x) {
print(environment())
f(whatever)
}
g(1:10)
The bottom line is you'll get back whatever you pass to f(whatever)
. That's why it's not a good idea to separate these functions. One work around would be to use
g <- function(...) deparse(f(...))
This way the parameter is passed through to f()
and not renamed in g()
.
On the other hand, g <- function(x) deparse(substitute(x)); g(1:10)
produces 1:10
because, in this case, substitute
is looking at promise object x
(in contrasts to the promise object whatever
in the above case). Promise x
here has the code 1:10
and the environment R_GlobalEnv
. (Again, this can be checked using g <- function(x) { print(promise_info(x) ; deparse(substitute(x))
. So substitute(x)
returns 1:10
as expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With