1

我想在项目中编写自定义迭代器。我可以从头开始执行此操作,但我更喜欢以标准方式执行此操作,因此其他人更容易阅读代码。该iterators包似乎是迭代器的标准方法,是使用 S3 编写的。不幸的是,我对 S3 对象没有太多的专业知识(尤其是如何在 S4 对象框架中使用 S3 对象的部分)。

我计划编写的迭代器将支持hasNext评估下一次迭代是否可能的方法,其中一些迭代器reset会将迭代重置为开始。

根据小插图,定义的“标准”迭代器iterators没有这个功能,所以我需要提供它。

问题:在我的场景中,最佳实践是什么?

  • 无论如何,我喜欢在 S4 对象框架中从头开始制作我的迭代器,并使用一些魔法(目前我不知道,但与此有关setOldClass)使它们与 S3 兼容。
  • 也许还有另一个标准的 S4 兼容迭代器包,我只是找不到?

显然,很高兴看到一些foreach具有自定义方法的工作兼容的 S4 迭代器示例,但至少请为我提供一个方向,在哪里投入精力来解决我的问题。

4

2 回答 2

3

iforever迭代器之后vignette("writing", package="iterators"),可以将 S3 类形式化为

setOldClass(c("abstractiter", "iter"))

然后创建一个派生类

.IForever <- setClass("IForever", contains=c("list", "abstractiter"))

使用构造函数

IForever <- function(x) 
{
    nextEl <- function() x
    obj <- list(nextElem=nextEl)
    .IForever(obj)
}

并使用

> nextElem(it)
[1] 42
> nextElem(it)
[1] 42
> unlist(as.list(it, n=6))
[1] 42 42 42 42 42 42

不过,我不确定这是否能让你获得那么多。迭代器使用闭包来提供状态,而这部分难题是由普通IForever函数提供的,而不是 S4 类系统的某些方面。S4 的形式主义可能会导致更好定义的 API,例如,在 AbstractIterator 基类上定义的方法,但这已经仅隐含在抽象器的 S3 实现中。

一个不同的(更好的)起点是指定所需方法的参考类

.RIterator <- setRefClass("RIterator",
    contains="abstractiter",
    methods=list(nextElem=function() stop("not implemented")))

迭代器可以在此之上实现,

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))

所以

> 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 
于 2013-06-09T19:55:00.427 回答
0

我认为你应该看一下这个itertools包,它有很多处理迭代器的功能,但主要是 S3。

但我认为编写这些功能的 S4 版本并不难。

根据您想要的包的设计和 api,您有很多选择。

更多信息在这里

例如,有一个hasNext功能(或适合您的方法)。

require(itertools)

it <- ihasNext(1:3)
hasNext(it)
## [1] TRUE

nextElem(it); nextElem(it); nextElem(it)
## [1] 1
## [1] 2
## [1] 3

hasNext(it)
## [1] FALSE
于 2013-06-09T16:56:12.790 回答