Following the iforever
iterator in vignette("writing", package="iterators")
, one could formalize the S3 class as
setOldClass(c("abstractiter", "iter"))
and then create a derived class
.IForever <- setClass("IForever", contains=c("list", "abstractiter"))
with a constructor
IForever <- function(x)
{
nextEl <- function() x
obj <- list(nextElem=nextEl)
.IForever(obj)
}
and use
> nextElem(it)
[1] 42
> nextElem(it)
[1] 42
> unlist(as.list(it, n=6))
[1] 42 42 42 42 42 42
I'm not sure that this is getting you that much, though. The iterator is using a closure to provide state, and this part of the puzzle is being provided by the plain IForever
function rather than some aspect of the S4 class system. The formalism of S4 might lead to a better-defined API, e.g., methods defined on an AbstractIterator base class, but that's already only implicit in the S3 implementation of abstractiter.
A different (better) starting point would be a reference class specifying a required method
.RIterator <- setRefClass("RIterator",
contains="abstractiter",
methods=list(nextElem=function() stop("not implemented")))
Iterators could be implemented on top of that,
LimitIterator <- setRefClass("SleepIterator",
fields=list(times="integer", .curr="integer"),
contains="RIterator",
methods=list(
initialize=function(...) initFields(..., .curr=1L),
nextElem=function() {
if (!hasNext())
stop("StopIteration")
.curr <<- .curr + 1L
invisible(NULL)
}, hasNext=function() .curr <= times))
so
> system.time(foreach(LimitIterator(times=8L)) %do% Sys.sleep(1L))
user system elapsed
0.052 0.000 8.057
> system.time(foreach(LimitIterator(times=8L)) %dopar% Sys.sleep(1L))
user system elapsed
0.084 0.436 1.261