| context | R Documentation |
Programming in R typically involves:
Making a context: assigning values to names.
Performing an action: evaluating an expression relative to a context.
let() and run() enable you to treat these procedures as reusable,
composable components.
let() makes a context: it lazily binds a sequence of ordered
named expressions to a child of a given environment (by default, the
current one).
For instance, in an environment env where z is in scope,
let(env, x = 1, y = x + 2, z = x * y * z)
is equivalent to calling
local({
x <- 1
y <- x + 2
z <- x * y * z
environment()
})
except let() binds the named expressions lazily (as
promises) and comprehends tidyverse
quasiquotation.
run() performs an action: it evaluates an expression relative to an
environment (by default, the current one) and, optionally, a sequence of
lazily evaluated ordered named expressions.
For instance, in an environment env where x is in scope,
run(env, x + y + z, y = x + 2, z = x * y * z)
is equivalent to calling
local({
y <- x + 2
z <- x * y * z
x + y + z
})
except run(), like let(), binds y and z lazily and comprehends
quasiquotation.
let(`_data` = parent.frame(), ...) run(`_data` = parent.frame(), `_expr`, ...)
_data |
Context of named values, namely an environment, list or data
frame; if a list or data frame, it is interpreted as an environment (like
the |
... |
Named expressions. An expression looks up values to the left of
it, and takes precedence over those in |
'_expr' |
Expression to evaluate (“run”). Quasiquotation is supported. |
run() returns the evaluation of `_expr` in the combined
environment of `_data` and ....
let() returns an environment where the bindings in ... are in scope, as
promises, as if they were assigned from left to right in
a child of the environment defined by `_data`.
Contexts, as made by let(), have an advantage over ordinary local
assignments because contexts are both lazy and composable. Like
assignments, the order of named expressions in a context is significant.
For example, you can string together contexts to make larger ones:
foo <-
let(a = ., b = a + 2) %>>>%
let(c = a + b) %>>>%
run(a + b + c)
foo(1)
#> [1] 8
Earlier bindings can be overriden by later ones:
bar <-
foo[1:2] %>>>% # Collect the contexts of 'foo()'
let(c = c - 1) %>>>% # Override 'c'
run(a + b + c)
bar(1)
#> [1] 7
Bindings are promises; they are only evaluated on demand:
run(let(x = a_big_expense(), y = "avoid a big expense"), y) #> [1] "avoid a big expense"
“Contexts” as described here should not be confused with “contexts” in R's internal mechanism.
with() is like run(), but more limited because it doesn't
support quasiquotation or provide a means to override local bindings.
# Miles-per-gallon of big cars
mtcars$mpg[mtcars$cyl == 8 & mtcars$disp > 350]
run(mtcars, mpg[cyl == 8 & disp > 350])
run(mtcars, mpg[big_cars], big_cars = cyl == 8 & disp > 350)
# 'let()' makes a reusable local context for big cars
cars <- let(mtcars, big = cyl == 8 & disp > 350)
eval(quote(mpg[big]), cars) # Quoting restricts name lookup to 'cars'
run(cars, mpg[big]) # The same, but shorter and more transparent
run(cars, wt[big])
mtcars$wt[mtcars$cyl == 8 & mtcars$disp > 350]
# Precedence of names is from right to left ("bottom-up"):
a <- 1000
run(`_expr` = a + b, a = 1, b = a + 2) # 4: all references are local
run(list(a = 1), a + b, b = a + 2) # 4: 'b' references local 'a'
run(let(a = 1, b = a + 2), a + b) # 4: 'b' references local 'a'
run(let(a = 1, b = a + 2), a + b, a = 0) # 3: latter 'a' takes precedence
run(list(a = 1, b = a + 2), a + b) # 1003: 'b' references global 'a'
# Bound expressions are lazily evaluated: no error unless 'x' is referenced
run(`_expr` = "S'all good, man.", x = stop("!"))
run(let(x = stop("!")), "S'all good, man.")
let(x = stop("!")) # Environment binding 'x'
try(let(x = stop("!"))$x) # Error: !
# Quasiquotation is supported
a <- 1
run(let(a = 2), a + !!a) #> [1] 3
run(let(a = 1 + !!a, b = a), c(a, b)) #> [1] 2 2
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.