Title: | Tools for Computing on the Language |
---|---|
Description: | Useful tools to pry back the covers of R and understand the language at a deeper level. |
Authors: | Hadley Wickham [aut, cre], R Core team [ctb] (Some code extracted from base R) |
Maintainer: | Hadley Wickham <[email protected]> |
License: | GPL-2 |
Version: | 0.1.6.9000 |
Built: | 2024-12-23 06:01:06 UTC |
Source: | https://github.com/hadley/pryr |
Infix form of makeActiveBinding
which creates an active
binding between a name and an expression: every time the name is accessed
the expression is recomputed.
x %<a-% value
x %<a-% value
x |
unquoted expression naming variable to create |
value |
unquoted expression to evaluate every time |
x %<a-% runif(1) x x x %<a-% runif(10) x x rm(x)
x %<a-% runif(1) x x x %<a-% runif(10) x x rm(x)
Infix wrapper for assign
+ lockBinding
that
creates a constant: a binding whose value can not be changed.
x %<c-% value
x %<c-% value
x |
unquoted expression naming variable to create |
value |
constant value |
x %<c-% 10 #' Generates an error: ## Not run: x <- 20 # Note that because of R's operator precedence rules, you # need to wrap compound RHS expressions in () y %<c-% 1 + 2 y z %<c-% (1 + 2) z
x %<c-% 10 #' Generates an error: ## Not run: x <- 20 # Note that because of R's operator precedence rules, you # need to wrap compound RHS expressions in () y %<c-% 1 + 2 y z %<c-% (1 + 2) z
Infix form of delayedAssign
which creates an delayed
or lazy binding, which only evaluates the expression the first time it is
used.
x %<d-% value
x %<d-% value
x |
unquoted expression naming variable to create |
value |
unquoted expression to evaluate the first time |
x %<d-% (a + b) a <- 10 b <- 100 x
x %<d-% (a + b) a <- 10 b <- 100 x
Print the byte-wise representation of a value
bytes(x, split = TRUE) bits(x, split = TRUE)
bytes(x, split = TRUE) bits(x, split = TRUE)
x |
An R vector of type |
split |
Whether we should split the output string at each byte. |
https://en.wikipedia.org/wiki/Two's_complement for more
information on the representation used for int
s.
https://en.wikipedia.org/wiki/IEEE_floating_point for more
information the floating-point representation used for double
s.
https://en.wikipedia.org/wiki/Character_encoding for an introduction
to character encoding, and ?Encoding
for more information on
how R handles character encoding.
## Encoding doesn't change the internal bytes used to represent characters; ## it just changes how they are interpretted! x <- y <- z <- "\u9b3c" Encoding(y) <- "bytes" Encoding(z) <- "latin1" print(x); print(y); print(z) bytes(x); bytes(y); bytes(z) bits(x); bits(y); bits(z) ## In R, integers are signed ints. The first bit indicates the sign, but ## values are stored in a two's complement representation. We see that ## NA_integer_ is really just the smallest negative integer that can be ## stored in 4 bytes bits(NA_integer_) ## There are multiple kinds of NAs, NaNs for real numbers ## (at least, on 64bit architectures) print( c(NA_real_, NA_real_ + 1) ) rbind( bytes(NA_real_), bytes(NA_real_ + 1) ) rbind( bytes(NaN), bytes(0/0) )
## Encoding doesn't change the internal bytes used to represent characters; ## it just changes how they are interpretted! x <- y <- z <- "\u9b3c" Encoding(y) <- "bytes" Encoding(z) <- "latin1" print(x); print(y); print(z) bytes(x); bytes(y); bytes(z) bits(x); bits(y); bits(z) ## In R, integers are signed ints. The first bit indicates the sign, but ## values are stored in a two's complement representation. We see that ## NA_integer_ is really just the smallest negative integer that can be ## stored in 4 bytes bits(NA_integer_) ## There are multiple kinds of NAs, NaNs for real numbers ## (at least, on 64bit architectures) print( c(NA_real_, NA_real_ + 1) ) rbind( bytes(NA_real_), bytes(NA_real_ + 1) ) rbind( bytes(NaN), bytes(0/0) )
call_tree
takes a quoted expression. ast
does the quoting
for you.
call_tree(x, width = getOption("width")) ast(x)
call_tree(x, width = getOption("width")) ast(x)
x |
quoted call, list of calls, or expression to display |
width |
displays width, defaults to current width as reported by
|
call_tree(quote(f(x, 1, g(), h(i())))) call_tree(quote(if (TRUE) 3 else 4)) call_tree(expression(1, 2, 3)) ast(f(x, 1, g(), h(i()))) ast(if (TRUE) 3 else 4) ast(function(a = 1, b = 2) {a + b}) ast(f()()())
call_tree(quote(f(x, 1, g(), h(i())))) call_tree(quote(if (TRUE) 3 else 4)) call_tree(expression(1, 2, 3)) ast(f(x, 1, g(), h(i()))) ast(if (TRUE) 3 else 4) ast(function(a = 1, b = 2) {a + b}) ast(f()()())
In infix and prefix forms.
compose(...) f %.% g
compose(...) f %.% g
... |
n functions to apply in order from right to left |
f , g
|
two functions to compose for the infix form |
not_null <- `!` %.% is.null not_null(4) not_null(NULL) add1 <- function(x) x + 1 compose(add1,add1)(8)
not_null <- `!` %.% is.null not_null(4) not_null(NULL) add1 <- function(x) x + 1 compose(add1,add1)(8)
Capture unevaluated dots.
dots(...) named_dots(...)
dots(...) named_dots(...)
... |
|
a list of expressions (not expression objects). named_dots
will use the deparsed expressions as default names.
y <- 2 str(dots(x = 1, y, z = )) str(named_dots(x = 1, y, z =))
y <- 2 str(dots(x = 1, y, z = )) str(named_dots(x = 1, y, z =))
This is a wrapper around environment
with a
consistent syntax.
enclosing_env(f)
enclosing_env(f)
f |
The name of a function. |
enclosing_env("plot") enclosing_env("t.test")
enclosing_env("plot") enclosing_env("t.test")
Deprecated: please use the lazyeval package instead.
explicit(x) eval2(x, data = NULL, env = parent.frame())
explicit(x) eval2(x, data = NULL, env = parent.frame())
x |
expression to make explicit, or to evaluate. |
data |
Data in which to evaluate code |
env |
Enclosing environment to use if data is a list or data frame. |
A compact syntax for anonymous functions.
f(..., .env = parent.frame())
f(..., .env = parent.frame())
... |
The last argument is the body of the function, all others are arguments to the function. If there is only one argument, the formals are guessed from the code. |
.env |
parent environment of the created function |
a function
f(x + y) f(x + y)(1, 10) f(x, y = 2, x + y) f({y <- runif(1); x + y})
f(x + y) f(x + y)(1, 10) f(x, y = 2, x + y) f({y <- runif(1); x + y})
Find a function with specified name.
fget(name, env = parent.frame())
fget(name, env = parent.frame())
name |
length one character vector giving name |
env |
environment to start search in. |
c <- 10 fget("c")
c <- 10 fget("c")
This is a flexible function that matches function component against
a regular expression, returning the name of the function if there are any
matches. fun_args
and fun_calls
are helper functions that
make it possible to search for functions with specified argument names, or
which call certain functions.
find_funs(env = parent.frame(), extract, pattern, ...) fun_calls(f) fun_args(f) fun_body(f)
find_funs(env = parent.frame(), extract, pattern, ...) fun_calls(f) fun_args(f) fun_body(f)
env |
environment in which to search for functions |
extract |
component of function to extract. Should be a function that
takes a function as input as returns a character vector as output,
like |
pattern |
stringr regular expression to results of |
... |
other arguments passed on to |
f |
function to extract information from |
find_funs("package:base", fun_calls, "match.fun", fixed = TRUE) find_funs("package:stats", fun_args, "^[A-Z]+$") fun_calls(match.call) fun_calls(write.csv) fun_body(write.csv) find_funs("package:utils", fun_body, "write", fixed = TRUE)
find_funs("package:base", fun_calls, "match.fun", fixed = TRUE) find_funs("package:stats", fun_args, "^[A-Z]+$") fun_calls(match.call) fun_calls(write.csv) fun_body(write.csv) find_funs("package:utils", fun_body, "write", fixed = TRUE)
Find all functions in that call supplied functions.
find_uses(envs, funs, match_any = TRUE)
find_uses(envs, funs, match_any = TRUE)
envs |
Vector of environments to look in. Can be specified by name, position or as environment |
funs |
Functions to look for |
match_any |
If |
names(find_uses("package:base", "sum")) envs <- c("package:base", "package:utils", "package:stats") funs <- c("match.call", "sys.call") find_uses(envs, funs)
names(find_uses("package:base", "sum")) envs <- c("package:base", "package:utils", "package:stats") funs <- c("match.call", "sys.call") find_uses(envs, funs)
This function figures out whether the input function is a regular/primitive/internal function, a internal/S3/S4 generic, or a S3/S4/RC method. This is function is slightly simplified as it's possible for a method from one class to be a generic for another class, but that seems like such a bad idea that hopefully no one has done it.
ftype(f)
ftype(f)
f |
unquoted function name |
a character of vector of length 1 or 2.
Other object inspection:
otype()
,
sexp_type()
ftype(`%in%`) ftype(sum) ftype(t.data.frame) ftype(t.test) # Tricky! ftype(writeLines) ftype(unlist)
ftype(`%in%`) ftype(sum) ftype(t.data.frame) ftype(t.test) # Tricky! ftype(writeLines) ftype(unlist)
Active binding info
is_active_binding(x)
is_active_binding(x)
x |
unquoted object name |
x <- 10 is_active_binding(x) x %<a-% runif(1) is_active_binding(x) y <- x is_active_binding(y)
x <- 10 is_active_binding(x) x %<a-% runif(1) is_active_binding(x) y <- x is_active_binding(y)
Promise info
is_promise(x) promise_info(x)
is_promise(x) promise_info(x)
x |
unquoted object name |
Other promise tools:
uneval()
x <- 10 is_promise(x) (function(x) is_promise(x))(x = 10)
x <- 10 is_promise(x) (function(x) is_promise(x))(x = 10)
Make and evaluate calls.
make_call(f, ..., .args = list()) do_call(f, ..., .args = list(), .env = parent.frame())
make_call(f, ..., .args = list()) do_call(f, ..., .args = list(), .env = parent.frame())
f |
Function to call. For |
... , .args
|
Arguments to the call either in or out of a list |
.env |
Environment in which to evaluate call. Defaults to parent frame. |
# f can either be a string, a symbol or a call make_call("f", a = 1) make_call(quote(f), a = 1) make_call(quote(f()), a = 1) #' Can supply arguments individual or in a list make_call(quote(f), a = 1, b = 2) make_call(quote(f), list(a = 1, b = 2))
# f can either be a string, a symbol or a call make_call("f", a = 1) make_call(quote(f), a = 1) make_call(quote(f()), a = 1) #' Can supply arguments individual or in a list make_call(quote(f), a = 1, b = 2) make_call(quote(f), list(a = 1, b = 2))
This constructs a new function given it's three components: list of arguments, body code and parent environment.
make_function(args, body, env = parent.frame())
make_function(args, body, env = parent.frame())
args |
A named list of default arguments. Note that if you want
arguments that don't have defaults, you'll need to use the special function
|
body |
A language object representing the code inside the function.
Usually this will be most easily generated with |
env |
The parent environment of the function, defaults to the calling
environment of |
f <- function(x) x + 3 g <- make_function(alist(x = ), quote(x + 3)) # The components of the functions are identical identical(formals(f), formals(g)) identical(body(f), body(g)) identical(environment(f), environment(g)) # But the functions are not identical because f has src code reference identical(f, g) attr(f, "srcref") <- NULL # Now they are: stopifnot(identical(f, g))
f <- function(x) x + 3 g <- make_function(alist(x = ), quote(x + 3)) # The components of the functions are identical identical(formals(f), formals(g)) identical(body(f), body(g)) identical(environment(f), environment(g)) # But the functions are not identical because f has src code reference identical(f, g) attr(f, "srcref") <- NULL # Now they are: stopifnot(identical(f, g))
Determine change in memory from running code
mem_change(code)
mem_change(code)
code |
Code to evaluate. |
Change in memory (in megabytes) before and after running code.
# Need about 4 mb to store 1 million integers mem_change(x <- 1:1e6) # We get that memory back when we delete it mem_change(rm(x))
# Need about 4 mb to store 1 million integers mem_change(x <- 1:1e6) # We get that memory back when we delete it mem_change(rm(x))
R breaks down memory usage into Vcells (memory used by vectors) and
Ncells (memory used by everything else). However, neither this distinction
nor the "gc trigger" and "max used" columns are typically important. What
we're usually most interested in is the the first column: the total memory
used. This function wraps around gc()
to return the total amount of
memory (in megabytes) currently used by R.
mem_used()
mem_used()
Megabytes of ram used by R objects.
mem_used()
mem_used()
Given a function class, find correspoding S4 method
method_from_call(call, env = parent.frame())
method_from_call(call, env = parent.frame())
call |
unquoted function call |
env |
environment in which to look for function definition |
library(stats4) # From example(mle) y <- c(26, 17, 13, 12, 20, 5, 9, 8, 5, 4, 8) nLL <- function(lambda) -sum(dpois(y, lambda, log = TRUE)) fit <- mle(nLL, start = list(lambda = 5), nobs = length(y)) method_from_call(summary(fit)) method_from_call(coef(fit)) method_from_call(length(fit))
library(stats4) # From example(mle) y <- c(26, 17, 13, 12, 20, 5, 9, 8, 5, 4, 8) nLL <- function(lambda) -sum(dpois(y, lambda, log = TRUE)) fit <- mle(nLL, start = list(lambda = 5), nobs = length(y)) method_from_call(summary(fit)) method_from_call(coef(fit)) method_from_call(length(fit))
Modify the arguments of a call.
modify_call(call, new_args)
modify_call(call, new_args)
call |
A call to modify. It is first standardised with
|
new_args |
A named list of expressions (constants, names or calls)
used to modify the call. Use |
call <- quote(mean(x, na.rm = TRUE)) # Modify an existing argument modify_call(call, list(na.rm = FALSE)) modify_call(call, list(x = quote(y))) # Remove an argument modify_call(call, list(na.rm = NULL)) # Add a new argument modify_call(call, list(trim = 0.1)) # Add an explicit missing argument modify_call(call, list(na.rm = quote(expr = )))
call <- quote(mean(x, na.rm = TRUE)) # Modify an existing argument modify_call(call, list(na.rm = FALSE)) modify_call(call, list(x = quote(y))) # Remove an argument modify_call(call, list(na.rm = NULL)) # Add a new argument modify_call(call, list(trim = 0.1)) # Add an explicit missing argument modify_call(call, list(na.rm = quote(expr = )))
Recursively modify a language object
modify_lang(x, f, ...)
modify_lang(x, f, ...)
x |
object to modify: should be a call, expression, function or list of the above. |
f |
function to apply to leaves |
... |
other arguments passed to |
a_to_b <- function(x) { if (is.name(x) && identical(x, quote(a))) return(quote(b)) x } examples <- list( quote(a <- 5), alist(a = 1, c = a), function(a = 1) a * 10, expression(a <- 1, a, f(a), f(a = a)) ) modify_lang(examples, a_to_b) # Modifies all objects called a, but doesn't modify arguments named a
a_to_b <- function(x) { if (is.name(x) && identical(x, quote(a))) return(quote(b)) x } examples <- list( quote(a <- 5), alist(a = 1, c = a), function(a = 1) a * 10, expression(a <- 1, a, f(a), f(a = a)) ) modify_lang(examples, a_to_b) # Modifies all objects called a, but doesn't modify arguments named a
object_size
works similarly to object.size
, but counts
more accurately and includes the size of environments. compare_size
makes it easy to compare the output of object_size
and
object.size
.
object_size(..., env = parent.frame()) compare_size(x)
object_size(..., env = parent.frame()) compare_size(x)
env |
Environment in which to terminate search. This defaults to the current environment so that you don't include the size of objects that are already stored elsewhere. |
x , ...
|
Set of objects to compute total size. |
An estimate of the size of the object, in bytes.
object_size
attempts to take into account the size of the
environments associated with an object. This is particularly important
for closures and formulas, since otherwise you may not realise that you've
accidentally captured a large object. However, it's easy to over count:
you don't want to include the size of every object in every environment
leading back to the emptyenv()
. object_size
takes
a heuristic approach: it never counts the size of the global env,
the base env, the empty env or any namespace.
Additionally, the env
argument allows you to specify another
environment at which to stop. This defaults to the environment from which
object_size
is called to prevent double-counting of objects created
elsewhere.
# object.size doesn't keep track of shared elements in an object # object_size does x <- 1:1e4 z <- list(x, x, x) compare_size(z) # this means that object_size is not transitive object_size(x) object_size(z) object_size(x, z) # object.size doesn't include the size of environments, which makes # it easy to miss objects that are carrying around large environments f <- function() { x <- 1:1e4 a ~ b } compare_size(f())
# object.size doesn't keep track of shared elements in an object # object_size does x <- 1:1e4 z <- list(x, x, x) compare_size(z) # this means that object_size is not transitive object_size(x) object_size(z) object_size(x, z) # object.size doesn't include the size of environments, which makes # it easy to miss objects that are carrying around large environments f <- function() { x <- 1:1e4 a ~ b } compare_size(f())
Determine object type.
otype(x)
otype(x)
x |
object to determine type of |
Figure out which object system an object belongs to:
base: no class attribute
S3: class attribute, but not S4
S4: isS4
, but not RC
RC: inherits from "refClass"
Other object inspection:
ftype()
,
sexp_type()
otype(data.frame()) otype(1:10)
otype(data.frame()) otype(1:10)
Find the parent (first) promise.
parent_promise(x)
parent_promise(x)
x |
unquoted name of promise to find initial value for for. |
f <- function(x) g(x) g <- function(y) h(y) h <- function(z) parent_promise(z) h(x + 1) g(x + 1) f(x + 1)
f <- function(x) g(x) g <- function(y) h(y) h <- function(z) parent_promise(z) h(x + 1) g(x + 1) f(x + 1)
Get parent/ancestor environment
parenv(env = parent.frame(), n = 1)
parenv(env = parent.frame(), n = 1)
env |
an environment |
n |
number of parents to go up |
adder <- function(x) function(y) x + y add2 <- adder(2) parenv(add2)
adder <- function(x) function(y) x + y add2 <- adder(2) parenv(add2)
envlist
of its
parent environments.If e
is not specified, it will start with environment from which
the function was called.
parenvs(e = parent.frame(), all = FALSE)
parenvs(e = parent.frame(), all = FALSE)
e |
An environment or other object. |
all |
If |
# Print the current environment and its parents parenvs() # Print the parent environments of the load_all function e <- parenvs(parenvs) e # Get all parent environments, going all the way to empty env e <- parenvs(parenvs, TRUE) e # Print e with paths print(e, path = TRUE) # Print the first 6 environments in the envlist e[1:6] # Print just the parent environment of load_all. # This is an envlist with one element. e[1] # Pull that environment out of the envlist and see what's in it. e[[1]] ls(e[[1]], all.names = TRUE)
# Print the current environment and its parents parenvs() # Print the parent environments of the load_all function e <- parenvs(parenvs) e # Get all parent environments, going all the way to empty env e <- parenvs(parenvs, TRUE) e # Print e with paths print(e, path = TRUE) # Print the first 6 environments in the envlist e[1:6] # Print just the parent environment of load_all. # This is an envlist with one element. e[1] # Pull that environment out of the envlist and see what's in it. e[[1]] ls(e[[1]], all.names = TRUE)
Partial function application allows you to modify a function by pre-filling some of the arguments. It is particularly useful in conjunction with functionals and other function operators.
partial(`_f`, ..., .env = parent.frame(), .lazy = TRUE)
partial(`_f`, ..., .env = parent.frame(), .lazy = TRUE)
_f |
a function. For the output source to read well, this should be an
be a named function. This argument has the weird (non-syntactic) name
|
... |
named arguments to |
.env |
the environment of the created function. Defaults to
|
.lazy |
If |
There are many ways to implement partial function application in R.
(see e.g. dots
in https://github.com/crowding/vadr for another
approach.) This implementation is based on creating functions that are as
similar as possible to the anonymous function that'd you'd create by hand,
if you weren't using partial
.
# Partial is designed to replace the use of anonymous functions for # filling in function arguments. Instead of: compact1 <- function(x) Filter(Negate(is.null), x) # we can write: compact2 <- partial(Filter, Negate(is.null)) # and the generated source code is very similar to what we made by hand compact1 compact2 # Note that the evaluation occurs "lazily" so that arguments will be # repeatedly evaluated f <- partial(runif, n = rpois(1, 5)) f f() f() # You can override this by saying .lazy = FALSE f <- partial(runif, n = rpois(1, 5), .lazy = FALSE) f f() f() # This also means that partial works fine with functions that do # non-standard evaluation my_long_variable <- 1:10 plot2 <- partial(plot, my_long_variable) plot2() plot2(runif(10), type = "l")
# Partial is designed to replace the use of anonymous functions for # filling in function arguments. Instead of: compact1 <- function(x) Filter(Negate(is.null), x) # we can write: compact2 <- partial(Filter, Negate(is.null)) # and the generated source code is very similar to what we made by hand compact1 compact2 # Note that the evaluation occurs "lazily" so that arguments will be # repeatedly evaluated f <- partial(runif, n = rpois(1, 5)) f f() f() # You can override this by saying .lazy = FALSE f <- partial(runif, n = rpois(1, 5), .lazy = FALSE) f f() f() # This also means that partial works fine with functions that do # non-standard evaluation my_long_variable <- 1:10 plot2 <- partial(plot, my_long_variable) plot2() plot2(runif(10), type = "l")
This function is similar to <<-
with two exceptions:
rebind(name, value, env = parent.frame())
rebind(name, value, env = parent.frame())
name |
name of existing binding to re-assign |
value |
new value |
env |
environment to start search in. |
if no existing binding is found, it throws an error
it does not recurse past the global environment into the attached packages
a <- 1 rebind("a", 2) a # Throws error if no existing binding ## Not run: rebind("b", 2) local({ rebind("a", 3) }) a # Can't find get because doesn't look past globalenv ## Not run: rebind("get", 1)
a <- 1 rebind("a", 2) a # Throws error if no existing binding ## Not run: rebind("b", 2) local({ rebind("a", 3) }) a # Can't find get because doesn't look past globalenv ## Not run: rebind("get", 1)
Performs ls
all the way up to a top-level environment (either
the parent of the global environment, the empty environment or a namespace
environment).
rls(env = parent.frame(), all.names = TRUE)
rls(env = parent.frame(), all.names = TRUE)
env |
environment to start the search at. Defaults to the
|
all.names |
Show all names, even those starting with |
Winston Chang
typename
determines the internal C typename, address
returns the memory location of the object, and refs
returns the
number of references pointing to the underlying object.
sexp_type(x) inspect(x, env = parent.frame()) refs(x) address(x) typename(x)
sexp_type(x) inspect(x, env = parent.frame()) refs(x) address(x) typename(x)
x |
name of object to inspect. This can not be a value. |
env |
When inspecting environments, don't go past this one. |
All functions uses non-standard evaluation to capture the symbol you are
referring to and the environment in which it lives. This means that you can
not call any of these functions on objects created in the function call.
All the underlying C level functions use Rf_findVar
to get to the
underlying SEXP.
Other object inspection:
ftype()
,
otype()
x <- 1:10 ## Not run: .Internal(inspect(x)) typename(x) refs(x) address(x) y <- 1L typename(y) z <- list(1:10) typename(z) delayedAssign("a", 1 + 2) typename(a) a typename(a) x <- 1:5 address(x) x[1] <- 3L address(x)
x <- 1:10 ## Not run: .Internal(inspect(x)) typename(x) refs(x) address(x) y <- 1L typename(y) z <- list(1:10) typename(z) delayedAssign("a", 1 + 2) typename(a) a typename(a) x <- 1:5 address(x) x[1] <- 3L address(x)
Opens a link to code search on github.
show_c_source(fun)
show_c_source(fun)
fun |
.Internal or .Primitive function call. |
show_c_source(.Internal(mean(x))) show_c_source(.Primitive(sum(x)))
show_c_source(.Internal(mean(x))) show_c_source(.Primitive(sum(x)))
Standardise a function call
standardise_call(call, env = parent.frame())
standardise_call(call, env = parent.frame())
call |
A call |
env |
Environment in which to look up call value. |
This version of substitute
is more suited for interactive
exploration because it will perform substitution in the global environment:
the regular version has a special case for the global environment where it
effectively works like quote
subs(x, env = parent.frame())
subs(x, env = parent.frame())
x |
a quoted call |
env |
an environment, or something that behaves like an environment
(like a list or data frame), or a reference to an environment (like a
positive integer or name, see |
Formally, substitution takes place by examining each name in the expression. If the name refers to:
an ordinary variable, it's replaced by the value of the variable.
a promise, it's replaced by the expression associated with the promise.
...
, it's replaced by the contents of ...
a <- 1 b <- 2 substitute(a + b) subs(a + b)
a <- 1 b <- 2 substitute(a + b) subs(a + b)
This version of substitute is needed because substitute
does not
evaluate it's first argument, and it's often useful to be able to modify
a quoted call.
substitute_q(x, env)
substitute_q(x, env)
x |
a quoted call |
env |
an environment, or something that behaves like an environment
(like a list or data frame), or a reference to an environment (like a
positive integer or name, see |
x <- quote(a + b) substitute(x, list(a = 1, b = 2)) substitute_q(x, list(a = 1, b = 2))
x <- quote(a + b) substitute(x, list(a = 1, b = 2)) substitute_q(x, list(a = 1, b = 2))
The title is somewhat misleading: rather than checking if an object is modified, this really checks to see if a name points to the same object.
track_copy(var, env = parent.frame(), quiet = FALSE)
track_copy(var, env = parent.frame(), quiet = FALSE)
var |
variable name (unquoted) |
env |
environment name in which to track changes |
quiet |
if |
a zero-arg function, that when called returns a boolean indicating if the object has changed since the last time this function was called
a <- 1:5 track_a <- track_copy(a) track_a() a[3] <- 3L track_a() a[3] <- 3 track_a() rm(a) track_a()
a <- 1:5 track_a <- track_copy(a) track_a() a[3] <- 3L track_a() a[3] <- 3 track_a() rm(a) track_a()
Unenclose a closure by substituting names for values found in the enclosing environment.
unenclose(f)
unenclose(f)
f |
a closure |
power <- function(exp) { function(x) x ^ exp } square <- power(2) cube <- power(3) square cube unenclose(square) unenclose(cube)
power <- function(exp) { function(x) x ^ exp } square <- power(2) cube <- power(3) square cube unenclose(square) unenclose(cube)
This is an alternative to subsitute that performs one job, and so gives a stronger signal regarding the intention of your code. It returns an error if the name is not associated with a promise.
uneval(x)
uneval(x)
x |
unquoted variable name that refers to a promise. An error will be thrown if it's not a promise. |
Other promise tools:
is_promise()
f <- function(x) { uneval(x) } f(a + b) f(1 + 4) delayedAssign("x", 1 + 4) uneval(x) x uneval(x)
f <- function(x) { uneval(x) } f(a + b) f(1 + 4) delayedAssign("x", 1 + 4) uneval(x) x uneval(x)
Implements the regular scoping rules, but instead of returning the value associated with a name, it returns the environment in which it is located.
where(name, env = parent.frame())
where(name, env = parent.frame())
name |
name, as string, to look for |
env |
environment to start at. Defaults to the calling environment of this function. |
x <- 1 where("x") where("t.test") where("mean") where("where")
x <- 1 where("x") where("t.test") where("mean") where("where")