R 迭代器示例崩溃

R iterator example crashes

这看起来很简单,所以我希望我做错了什么。我升级了 R 和包,但现在我从 运行 包 PDF 中的示例中得到以下内容:

> sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur 11.6

Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] iterators_1.0.13

loaded via a namespace (and not attached):
[1] compiler_4.1.2   parallel_4.1.2   tools_4.1.2      itertools_0.1-3  rJava_1.0-5     
> library(iterators)
> i1 <- iter(1:3)
> nextElem(i1)
Error: StopIteration
>

我没有看到任何其他帖子指出最新版本中的某些内容已损坏,所以我不知道该从哪里开始。感谢您提供任何帮助、链接或信息!

当您 运行 iter(1:3) 时,R 调度到默认方法 iter.default,其定义为:

function (obj, checkFunc = function(...) TRUE, recycle = FALSE, ...) {
  state <- new.env()
  state$i <- 0L
  state$obj <- obj
  n <- length(obj)
  it <- list(state = state, length = n, checkFunc = checkFunc, recycle = recycle)
  class(it) <- c("containeriter", "iter")
  it
}

iter.default 不会执行任何可能导致重复评估结果不同的异常操作(例如,随机数生成)。这意味着 iter(1:3) 应该 始终 评估包含环境 state 绑定 i = 0L.

的命名列表

在我系统上的 vanilla R (4.1.2) 会话中,它就是这样做的:

$ R --vanilla
> library("iterators")
> x <- iter(1:3)
> x$state$i
[1] 0

但在 RStudio(桌面版 1.4.1717)中:

> library("iterators")
> x <- iter(1:3)
> x$state$i
[1] 4

这解释了 nextElem 抛出的 StopIteration 错误; 参见nextElem.containeriter here的定义(有点长,无法粘贴)。

运行iter(1:3)在一个调试器下,一行一行,我确定环境statei的值由0L变为4L 在行 class(it) <- c("containeriter", "iter").

> library("iterators")
> debugonce(iterators:::iter.default)
> iter(1:3)
debugging in: iter.default(1:3)
debug: {
    state <- new.env()
    state$i <- 0L
    state$obj <- obj
    n <- length(obj)
    it <- list(state = state, length = n, checkFunc = checkFunc, 
        recycle = recycle)
    class(it) <- c("containeriter", "iter")
    it
}
Browse[2]> n
debug: state <- new.env()
Browse[2]> n
debug: state$i <- 0L
Browse[2]> n
debug: state$obj <- obj
Browse[2]> n
debug: n <- length(obj)
Browse[2]> n
debug: it <- list(state = state, length = n, checkFunc = checkFunc, 
    recycle = recycle)
Browse[2]> n
debug: class(it) <- c("containeriter", "iter")
Browse[2]> state$i
[1] 0
Browse[2]> n
debug: it
Browse[2]> state$i
[1] 4
Browse[2]>

(这里,n 是给调试器 运行 n ext line 的指令,不是 R 代码。)

有趣的是,当我们访问 iter(1:3) 的结果而不将其分配给名称时,可以避免故障。

> iter(1:3)$state$i
[1] 0

当我们完全移除 iterators 界面时, 避免了故障。

> f <- function (obj, checkFunc = function(...) TRUE, recycle = FALSE, ...) {
+   state <- new.env()
+   state$i <- 0L
+   state$obj <- obj
+   n <- length(obj)
+   it <- list(state = state, length = n, checkFunc = checkFunc, recycle = recycle)
+   class(it) <- c("containeriter", "iter")
+   it
+ }
> x <- f(1:3)
> x$state$i
[1] 0

所以,似乎是内存损坏,原因似乎是iterators之间的相互作用和 RStudio。可能值得将问题报告给 RStudio here [edit: I've just done this; see here] and keeping the iterators maintainers in the loop here [edit: I've also done this; see here)。

FWIW,这是我的系统详细信息。尚不清楚问题是否与平台有关...

R version 4.1.2 (2021-11-01)
Platform: aarch64-apple-darwin20.6.0 (64-bit)
Running under: macOS Big Sur 11.6.1

更新

一位 RStudio 工程师回复了我的 issue。以下是他们回应的详细说明:

默认情况下,RStudio 在绑定到全局环境中的对象上静默调用 str,以便使用有用的信息填充环境窗格。因此,一旦您在全局环境中分配 x <- iter(1:3)

  1. RStudio 静默评估 str(x)
  2. R 调度到 str.default
  3. str.default 计算 vapply(x, typeof, "").
  4. vapply 计算 as.list(x).
  5. R 调度到 as.list.iter(源代码 here)。
  6. as.list.iter 遍历 1:3.
  7. 的所有元素

因此,x$state$i 的值以 1:3 的长度加一结束。

我们可以像这样在 vanilla R 中重现 RStudio 的行为:

$ R --vanilla
> library("iterators")
> x <- iter(1:3)
> x$state$i
[1] 0
> as.list(x)
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 3

> x$state$i
[1] 4

要避开 RStudio 的自动 str,您可以将环境窗格设置为“仅手动刷新”。更永久的修复将要求 iterators 维护者为 class "iter" 编写 str 方法或重构他们的 as.list 方法。