R中的惰性序列
在Clojure中,使用惰性序列构造函数很容易创建无限序列。比如说,R中的惰性序列,r,clojure,lazy-sequences,R,Clojure,Lazy Sequences,在Clojure中,使用惰性序列构造函数很容易创建无限序列。比如说, (def N (iterate inc 0)) 返回与无限序列等价的数据对象N (0 1 2 3 ...) 计算值N会导致无限循环。计算(取20 N)返回前20个数字。由于序列是惰性的,因此只有在您要求时才迭代inc函数。由于Clojure是同象的,所以惰性序列是递归存储的 在R中,有可能做类似的事情吗?你能给出一些示例R代码,它生成一个数据对象N,它等价于自然数的完整无限序列吗?对完整对象N求值应该会产生一个循环,但是像
(def N (iterate inc 0))
返回与无限序列等价的数据对象N
(0 1 2 3 ...)
计算值
N
会导致无限循环。计算(取20 N)
返回前20个数字。由于序列是惰性的,因此只有在您要求时才迭代inc
函数。由于Clojure是同象的,所以惰性序列是递归存储的
在R中,有可能做类似的事情吗?你能给出一些示例R代码,它生成一个数据对象N
,它等价于自然数的完整无限序列吗?对完整对象N
求值应该会产生一个循环,但是像head(N)
这样的东西应该只返回前导数字
注意:我对惰性序列而不是自然数本身更感兴趣
编辑:这是惰性seq
的Clojure源代码:
(defmacro lazy-seq
"Takes a body of expressions that returns an ISeq or nil, and yields
a Seqable object that will invoke the body only the first time seq
is called, and will cache the result and return it on all subsequent
seq calls. See also - realized?"
{:added "1.0"}
[& body]
(list 'new 'clojure.lang.LazySeq (list* '^{:once true} fn* [] body)))
我正在寻找一个在R中具有相同功能的宏。迭代器
库可能能够实现您想要的:
library(iterators)
i <- icount()
nextElem(i)
# 1
nextElem(i)
# 2
库(迭代器)
我你没有说明你的目标,所以我要指出,用任何语言
j <- 1
while(TRUE) { x[j+1] <- x[j]+1 ; j<-j+1}
j因为R在向量上工作得最好,所以人们似乎经常需要一个返回向量的迭代器
library(iterators)
ichunk <- function(n, ..., chunkSize) {
it <- icount(n) # FIXME: scale by chunkSize
chunk <- seq_len(chunkSize)
structure(list(nextElem=function() {
(nextElem(it) - 1L) * chunkSize + chunk
}), class=c("abstractiter", "iter"))
}
库(迭代器)
ichunk替代实现
介绍
自从这篇文章发表以来,我有机会更频繁地在R中工作,所以我提供了一个备用的BaseR实现。我再次怀疑,通过降低到C扩展级别,您可以获得更好的性能。休息后再回答原来的问题
基本R中的第一个挑战是缺少真正的cons
(即在R级别公开)。R使用c
进行混合cons/concat操作,但这不会创建一个链表,而是创建一个新的向量,该向量包含两个参数的元素。特别是,必须知道两个参数的长度,而惰性序列则不是这样。此外,连续的c
操作表现出二次性能,而不是恒定时间。现在,您可以使用长度为2的“列表”(实际上是向量,而不是链表)来模拟cons单元格,但是
第二个挑战是在数据结构中强制承诺。R有一些使用隐式承诺的惰性评估语义,但这些都是二等公民。用于返回显式承诺的函数(delay
)已被弃用,取而代之的是implicitdelayedAssign
,该函数仅针对其副作用执行--“未评估的承诺永远不应可见”。函数参数是隐式承诺,因此您可以使用它们,但是,如果不强制执行,就无法将承诺放入数据结构中
CS 101
事实证明,这两个挑战可以通过回顾计算机科学101来解决。数据结构可以通过闭包实现
cons <- function(h,t) function(x) if(x) h else t
first <- function(kons) kons(TRUE)
rest <- function(kons) kons(FALSE)
cons“输出自然数的完整无限序列”?你有无限的时间吗?“因为Clojure是同象的,所以惰性序列是递归存储的”这句话没有任何意义。同源性在这里一点都不相关,而且“递归存储”的含义也不是100%清楚。@amalloy:数据(0 1 2 3…)和代码(iterate inc 0)作为值是等价的。所谓“递归存储”,我的意思是无限序列由一个递归公式(使用iterate)表示,该公式可用于计算完整序列。我认为您可能用一些不相关的Clojure术语抛弃了R
用户。用一些嘲弄的例子可能会更清楚这一点;斐波那契总是一个很好的例子。无论如何,我已经尝试了一个答案,至少可以证明你在问什么。您可能会问R
专家是否有更好的方法来完成我所做的工作。我想它最终会溢出。根据它的第一个参数的文档:“计数:迭代器将触发的次数。如果没有指定,它将永远计数。”
。“唯一的解释是魔法。”斯科特里奇,我重写了我的问题,使意图更加清楚。感谢您对迭代器
库的精彩引用。对于重写的问题,你认为这个库一般可以用来构造惰性序列吗?我认为@A.Webb的答案就是你想要的答案这是一个很棒的答案,@A.Webb。它工作得很好。非常感谢。该包使您能够创建Quosure,它充当“第一类承诺”——它们捕获表达式和计算上下文,但与承诺不同的是,它们在引用时会重新计算。
fibonacci <- function(a,b) cons(a, fibonacci(b, a+b))
fibs <- fibonacci(1,1)
filterz <- function(pred, s) {
if(is.null(s)) return(NULL)
f <- first(s)
r <- rest(s)
if(pred(f)) cons(f, filterz(pred, r)) else filterz(pred, r) }
take_whilez <- function(pred, s) {
if(is.null(s) || !pred(first(s))) return(NULL)
cons(first(s), take_whilez(pred, rest(s))) }
reduce <- function(f, init, s) {
r <- init
while(!is.null(s)) {
r <- f(r, first(s))
s <- rest(s) }
return(r) }
reduce(`+`, 0, filterz(function(x) x %% 2 == 0, take_whilez(function(x) x < 4e6, fibs)))
# [1] 4613732
numbers <- function(x) as.LazySeq(c(x, numbers(x+1)))
nums <- numbers(1)
take(10,nums)
#=> [1] 1 2 3 4 5 6 7 8 9 10
#Slow, but does not overflow the stack (level stack consumption)
sum(take(100000,nums))
#=> [1] 5000050000
fibonacci <- function(a,b) {
as.LazySeq(c(a, fibonacci(b, a+b)))}
fibs <- fibonacci(1,1)
take(10, fibs)
#=> [1] 1 1 2 3 5 8 13 21 34 55
nth(fibs, 20)
#=> [1] 6765
is.LazySeq <- function(x) inherits(x, "LazySeq")
as.LazySeq <- function(s) {
cache <- NULL
value <- function() {
if (is.null(cache)) {
cache <<- force(s)
while (is.LazySeq(cache)) cache <<- cache()}
cache}
structure(value, class="LazySeq")}
first <- function(x) UseMethod("first", x)
rest <- function(x) UseMethod("rest", x)
first.default <- function(s) s[1]
rest.default <- function(s) s[-1]
first.LazySeq <- function(s) s()[[1]]
rest.LazySeq <- function(s) s()[[-1]]
nth <- function(s, n) {
while (n > 1) {
n <- n - 1
s <- rest(s) }
first(s) }
#Note: Clojure's take is also lazy, this one is "eager"
take <- function(n, s) {
r <- NULL
while (n > 0) {
n <- n - 1
r <- c(r, first(s))
s <- rest(s) }
r}