Tailcall {base} | R Documentation |
Tailcall
and Exec
Description
Tailcall
and Exec
allow writing more
stack-space-efficient recursive functions in R.
Usage
Tailcall(FUN, ...)
Exec(expr, envir)
Arguments
FUN |
a function or a non-empty character string naming the function to be called. |
... |
all the arguments to be passed. |
expr |
a call expression. |
envir |
environment for evaluating |
Details
Tailcall
evaluates a call to FUN
with arguments ... in
the current environment, and Exec
evaluates the call
expr
in environment envir
. If a Tailcall
or
Exec
expression appears in tail position in an R function, and
if there are no on.exit
expressions set, then the evaluation
context of the new calls replaces the currently executing call context
with a new one. If the requirements for context re-use are not met,
then evaluation proceeds in the standard way adding another context to
the stack.
Using Tailcall
it is possible to define tail-recursive
functions that do not grow the evaluation stack. Exec
can be
used to simplify the call stack for functions that create and then
evaluate an expression.
Because of lazy evaluation of arguments in R it may be necessary to force evaluation of some arguments to avoid accumulating deferred evaluations.
This tail call optimization has the advantage of not growing
the call stack and permitting arbitrarily deep tail recursions. It
does also mean that stack traces produced by traceback
or sys.calls
will only show the call specified by
Tailcall
or Exec
, not the previous call whose stack
entry has been replaced.
Note
Tailcall
and Exec
are experimental and may be
changed or dropped in future released versions of R.
See Also
Examples
## tail-recursive log10-factorial
lfact <- function(n) {
lfact_iter <- function(val, n) {
if (n <= 0)
val
else {
val <- val + log10(n) # forces val
Tailcall(lfact_iter, val, n - 1)
}
}
lfact_iter(0, n)
}
10 ^ lfact(3)
lfact(100000)
## simplified variant of do.call using Exec:
docall <- function (what, args, quote = FALSE) {
if (!is.list(args))
stop("second argument must be a list")
if (quote)
args <- lapply(args, enquote)
Exec(as.call(c(list(substitute(what)), args)), parent.frame())
}
## the call stack does not contain the call to docall:
docall(function() sys.calls(), list()) |>
Find(function(x) identical(x[[1]], quote(docall)), x = _)
## contrast to do.call:
do.call(function(x) sys.calls(), list()) |>
Find(function(x) identical(x[[1]], quote(do.call)), x = _)