如何用 Eager 语言制作惰性列表?

发布于 2024-07-23 21:01:49 字数 651 浏览 10 评论 0原文

我想在Scheme中制作一个惰性列表。 这是我到目前为止所拥有的。

;; Constructor for Pairs
(define (cons-stream a b)
  (cons a (λ() b)))

;; Selectors
(define (car-stream a-stream)
  (car a-stream))

(define (cdr-stream a-stream)
  ((cdr a-stream)))

;; Lazy List using the pairs
(define (lazy-list from)
  (cons-stream from (lazy-list (+ from 1))))

;; Take function
(define (take a-lazy-list number)
  (define (take-helper i )
    (if(= i number)
       empty
       (cons (car a-lazy-list) (take-helper (+ i 1)))))
  (take-helper 0))

惰性列表的问题在于,Scheme 首先计算内部表达式(惰性列表(+ from 1)),导致过程进入无限递归。

有没有办法让 con-stream 接受这个内部表达式而不进行任何评估?

I wanted to make a lazy list in Scheme. This is what I have so far.

;; Constructor for Pairs
(define (cons-stream a b)
  (cons a (λ() b)))

;; Selectors
(define (car-stream a-stream)
  (car a-stream))

(define (cdr-stream a-stream)
  ((cdr a-stream)))

;; Lazy List using the pairs
(define (lazy-list from)
  (cons-stream from (lazy-list (+ from 1))))

;; Take function
(define (take a-lazy-list number)
  (define (take-helper i )
    (if(= i number)
       empty
       (cons (car a-lazy-list) (take-helper (+ i 1)))))
  (take-helper 0))

The problem with the lazy-list is that Scheme evaluates the inner expression (lazy-list (+ from 1)) first causing the procedure to go into an infinite recursion.

Is there a way to make the con-stream take this inner expression without any evaluation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心病无药医 2024-07-30 21:01:49

解决方案是使用宏。 我不是方案专家(尤其不是宏专家),但也许这个片段可以作为灵感:

(define-syntax pointer-to
   (syntax-rules ()
    ((pointer-to var)
     (make-pointer
      (lambda () var) ; the "pointer dereference" operation
      (lambda (value) (set! var value)))))) ; "pointer write"

它的用法如下:

(define x 1)
(define px (pointer-to x))
(pointer-set! px 2) ; this (in some sense) becomes `(set! x 2)'

所以也许你想要类似的东西

(define-syntax lazy-cons
 (syntax-rules ()
  ((lazy-cons head lazytail)
   (cons head (lambda () lazytail)))))

但我不确定。 看看define-syntax

The solution is to use a macro. I'm no scheme expert (especially not on macros), but maybe this snippet can serve as inspiration:

(define-syntax pointer-to
   (syntax-rules ()
    ((pointer-to var)
     (make-pointer
      (lambda () var) ; the "pointer dereference" operation
      (lambda (value) (set! var value)))))) ; "pointer write"

It's used as follows:

(define x 1)
(define px (pointer-to x))
(pointer-set! px 2) ; this (in some sense) becomes `(set! x 2)'

So maybe you want something like

(define-syntax lazy-cons
 (syntax-rules ()
  ((lazy-cons head lazytail)
   (cons head (lambda () lazytail)))))

But I'm not sure. Have a look at define-syntax.

千寻… 2024-07-30 21:01:49

如果你不想走宏观路线,你总是可以放弃 cons-stream 并重写 lazy-list 如下:

(define (lazy-list from)
  (cons from (λ() (lazy-list (+ from 1)))))

这可能是最简单、最实用的解决方案,但它只适用于制作递增数字的惰性列表。 您可以通过传入一个函数来概括这一点,该函数在调用时将生成列表的连续元素:

(define (lazy-list-gen generator)
  (cons (generator)
        (λ() (lazy-list-gen generator))))

(define (lazy-list from)
  (lazy-list-gen
   (λ()
     (let ((ret from))
       (set! from (+ from 1))
       ret))))

这工作得很好:

> (define x (lazy-list 1))
> (car-stream x)
1
> (car-stream (cdr-stream x))
2

但是代码中有一个错误:

... continuing from above ...
> (car-stream (cdr-stream x))
3

发生此错误是因为调用 cdr-stream再次调用生成器。 我们可以通过缓存 lambda 的返回值来解决这个问题:

(define (lazy-list-gen generator)
  (cons (generator)
        (let ((gen-cache #f))
          (λ()
            (cond ((not gen-cache)
                   (set! gen-cache (lazy-list-gen generator))))
            gen-cache))))

现在它可以正常工作了:

> (define x (lazy-list 1))
> (car-stream x)
1
> (car-stream (cdr-stream x))
2
> (car-stream (cdr-stream x))
2
> (car-stream (cdr-stream (cdr-stream x)))
3
> (car-stream (cdr-stream x))
2

If you don't want to go the macro route, you could always just abandon cons-stream and rewrite lazy-list as follows:

(define (lazy-list from)
  (cons from (λ() (lazy-list (+ from 1)))))

This is probably the easiest, most pragmatic solution, but it's only good for making lazy lists of incrementing numbers. You could generalize this by passing in a function that will generate successive elements of the list when called:

(define (lazy-list-gen generator)
  (cons (generator)
        (λ() (lazy-list-gen generator))))

(define (lazy-list from)
  (lazy-list-gen
   (λ()
     (let ((ret from))
       (set! from (+ from 1))
       ret))))

This works pretty well:

> (define x (lazy-list 1))
> (car-stream x)
1
> (car-stream (cdr-stream x))
2

But there's a bug in the code:

... continuing from above ...
> (car-stream (cdr-stream x))
3

This error happens because the call to cdr-stream calls generator again. We can solve this by caching the return value of the lambda:

(define (lazy-list-gen generator)
  (cons (generator)
        (let ((gen-cache #f))
          (λ()
            (cond ((not gen-cache)
                   (set! gen-cache (lazy-list-gen generator))))
            gen-cache))))

Now it works as it should:

> (define x (lazy-list 1))
> (car-stream x)
1
> (car-stream (cdr-stream x))
2
> (car-stream (cdr-stream x))
2
> (car-stream (cdr-stream (cdr-stream x)))
3
> (car-stream (cdr-stream x))
2
倾听心声的旋律 2024-07-30 21:01:49

Scheme 中的惰性列表称为这是标准介绍。

A lazy list in Scheme is known as a stream. Here's the standard introduction.

初相遇 2024-07-30 21:01:49

你真的应该看看 SRFI-41

特别是,由在急切的语言中,递归函数会严重泄漏内存,除非您特别避免它。 为此,您还需要使递归函数变得懒惰,而不是急切。 这意味着您的惰性实现需要是SRFI-45,它导出延迟、强迫、懒惰。 递归构建流的函数必须将其主体包装在惰性中。

You should really look at SRFI-41

In particular, lazy streams created by recursive functions will leak memory badly in an eager language, unless you specifically avoid it. To do so, you need to make recursive functions lazy also, not eager. This means that your laziness implementation needs to be SRFI-45, which exports delay, force, and lazy. Functions that build streams recursively must wrap their bodies in lazy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文