gen {async} | R Documentation |
gen({...})
with an expression written in its argument, creates a
generator, an object which computes an indefinite sequence.
When written inside a generator expression, yield(expr)
causes the
generator to return the given value, then pause until the next value is
requested.
When running in a generator expression, yieldFrom(it))
, given
a list or iteror in its argument, will yield successive values from that
iteror until it is exhausted, then continue.
gen(
expr,
...,
split_pipes = FALSE,
compileLevel = getOption("async.compileLevel")
)
yield(expr)
yieldFrom(it, err)
expr |
An expression, to be turned into an iterator. |
... |
Undocumented. |
split_pipes |
Silently rewrite expressions where "yield" appears in chained calls. See async. |
compileLevel |
Current levels are 0 (no compilation) or -1 (name munging only). |
it |
A list, iteror or compatible object. |
err |
An error handler |
On the "inside", that is the point of view of code you write in
{...}
, is ordinary sequential code using conditionals, branches,
loops and such, outputting one value after another with yield()
.
For example, this code creates a generator that computes a random
walk:
rwalk <- gen({ x <- 0; repeat { x <- x + rnorm(1) yield(x) } })
On the "outside," that is, the object returned by gen()
, a
generator behaves like an iterator over an indefinite
collection. So we can collect the first 100 values from the above
generator and compute their mean:
rwalk |> itertools2::take(100) |> as.numeric() |> mean()
When nextOr(rwalk, ...)
is called, the generator executes its
"inside" expression, in a local environment, until it reaches a
call to yield().
THe generator 'pauses', preserving its execution
state, and nextElem
then returns what was passed to yield
. The
next time nextElem(rwalk)
is called, the generator resumes
executing its inside expression starting after the yield()
.
If you call gen
with a function expression, as in:
gseq <- gen(function(x) for (i in 1:x) yield(i))
then instead of returning a single generator it will return a generator function (i.e. a function that constructs and returns a generator.) The above is morally equivalent to:
gseq <- function(x) {force(x); gen(for (i in 1:x) yield(i))}
so the generator function syntax just saves you writing the force call.
A generator expression can use any R functions, but a call to
yield
may only appear in the arguments of a "pausable" function.
The async
package has several built-in pausable functions corresponding
to base R's control flow functions, such as if
, while
, tryCatch
,
<-
, {}
, ||
and so on (see pausables for more details.) A call
to yield
may only appear in an argument of one of these pausable
functions. So this random walk generator:
rwalk <- gen({x <- 0; repeat {x <- yield(x + rnorm(1))}})
is legal, because yield
appears within arguments to {}
,
repeat
, and <-
, for which this package has pausable
definitions. However, this:
rwalk <- gen({x <- rnorm(1); repeat {x <- rnorm(1) + yield(x)}})
is not legal, because yield
appears in an argument to +
, which
does not have a pausable definition.
'gen(...) returns an iteror.
yield(x)
returns the same value x.
yieldFrom returns NULL, invisibly.
i_chain <- function(...) {
iterators <- list(...)
gen(for (it in iterators) yieldFrom(it))
}