在Racket中为惰性列表构建累加器

时间:2014-04-23 14:45:12

标签: scheme racket lazy-evaluation

我定义了一个从零开始的所有整数的简单惰性列表:

(define integers-from
  (lambda (n) 
    (cons n
          (lambda () (integers-from (+ 1 n))))))

(define lz (integers-from 0))

我还编写了一个将懒惰列表作为参数

的控制器
(define lz-lst-accumulate
  (lambda (op initial lz)
    (if (null? lz)
        initial
        (cons (op (head lz) initial)  
              (lambda () (lz-lst-accumulate op (op initial (head lz)) (tail lz))))))) 

这个控告者是否回答了懒惰列表的格式? 这是对累加器的简单测试:

(define acc (lz-lst-accumulate * 1 lz))
(take acc 4)
=> '(1 2 6 24)

take是一个辅助函数,它从惰性列表的第一个n元素创建一个列表:

(define head car)

(define tail
  (lambda (lz-lst)
     ((cdr lz-lst)) ))

(define take
  (lambda (lz-lst n)
    (if (= n 0)
        (list)
        (cons (car lz-lst)
              (take (tail lz-lst) (sub1 n)))) ))

1 个答案:

答案 0 :(得分:5)

lz-lst-accumulate中,您计算​​一次(op (head lz) initial),然后再计算(op initial (head lz))。这是不一致的;两者应该是相同的,实际只计算一次,因为它是相同的值:

(define lz-lst-accumulate
  (lambda (op initial lz)
    (if (lz-lst-empty? lz)
        initial
        (let ((val (op (head lz) initial)))
           (cons val
              (lambda () (lz-lst-accumulate op val (tail lz))))))))

仅在您的示例中使用数字,因为您使用了类型对称操作*。使用cons它将无效。

除此之外没关系。实际上,lz-lst-accumulate在Haskell中通常被称为left foldscanl,因为您生成了“累积”值foldl f z xs = last (scanl f z xs))的进展。


re:您的take版本,它正在强制流的一个太多元素。更好地做到这一点

(define take
  (lambda (lz n)
    (if (or (<= n 0) (lz-lst-empty? lz))
      (list)
      (if (= n 1)
        (list (car lz))      ; already forced
        (cons (car lz)
              (take (tail lz) (sub1 n)))))))

因此它只会强制生成尽可能多的元素,而不是一个元素(可能是例如(/ 1 0)的分歧,无缘无故地使整个计算无效)。

这样,SRFI 41 (take 4 (stream-map 1/ (ints-from-by 4 -1))) 中的反例就会正常工作 (它会在不强制(1/4 1/3 1/2 1/1)的情况下计算1/0take的正常版本(就像您正在使用的版本一样)会这样做。