2016-08-04 69 views
2

我写了一个代码,它使用延迟评估来产生无限的数据结构,但有一个错误。如何在Guile方案中使用懒惰评估?

下面是代码:

#!/usr/bin/guile \ 
-e main -s 
!# 
(define ints-f 
    (lambda (n) 
     (let ((n1 (delay (ints-f (+ n 1))))) 
     (cons n (force n1))))) 
(define (main args) 
    (display (car (ints-f 3))) 
    (newline) 
    ) 

这使得它说堆栈溢出错误。这意味着即使没有呼叫,力量也正在执行。如何纠正这一点?

#!/usr/bin/guile \ 
-e main -s 
!# 
(define ints-f 
    (lambda (n) 
     (let ((n1 (delay (ints-f (+ n 1))))) 
     (cons n n1)))) 
(define (main args) 
    (display (car (ints-f 3))) 
    (newline) 
    ) 

上面的代码是给3个预期的输出,但如果我在代码中使用的CDR如下

#!/usr/bin/guile \ 
-e main -s 
!# 
(define ints-f 
    (lambda (n) 
     (let ((n1 (delay (ints-f (+ n 1))))) 
     (cons n n1)))) 
(define (main args) 
    (display (cdr (ints-f 3))) 
    (newline) 
    ) 

它打印一个承诺对象。

如何在guile方案中使用懒惰评估?

+1

如果你立即'延迟'然后'强制',那么你就绕过了承诺的懒惰属性。 'delay'形式有效地创建一个thunk,'force'调用它。唯一的区别是承诺会缓存结果,因此两次强制承诺不会重新计算计算结果。整体语言的语义仍然完全严格。 –

回答

1

第一个代码片段不正确,您在构建序列时强制使用该值,从而破坏了懒惰评估的整个目的。在另一方面,第二片段看起来正确的 - 虽然它可以简化一下:

(define (ints-f n) 
    (cons n (delay (ints-f (+ n 1))))) 

这是正常得到调用cdr当一个承诺,它与delay建立一个thunk只会产生它的价值当force d。事实上,如果你想窥视到一个无限序列的元素,你将不得不使用一个程序遍历部分你有兴趣:

(define (take seq n) 
    (if (= n 0) 
     '() 
     (cons (car seq) 
      (take (force (cdr seq)) (- n 1))))) 

同理:

(define (get seq idx) 
    (if (= idx 0) 
     (car seq) 
     (get (force (cdr seq)) (- idx 1)))) 

例如:

(take (ints-f 5) 5) 
=> '(5 6 7 8 9) 

(get (ints-f 5) 5) 
=> 10 
+0

你的'take'是一个经典的过度强调一个额外元素的序列。 '(取seq 1)'应该等同于'(list(car seq))',而不是'(begin(force(cdr seq))(list(car seq)))''。 :) –

1

您是否在尝试创建流?你可能希望参考(srfi srfi-41)模块来实现它。 (披露:我写的模块代码的具体狡诈零部件;一切从the reference implementation移植)

(use-modules (srfi srfi-41)) 
(define-stream (ints-f n) 
    (stream-cons n (ints-f (1+ n)))) 

注意define-streamstream-cons是共同打造的(SRFI 45风格)宏delay/force幕后。

用例:

> (stream->list 10 (ints-f 100)) 
(100 101 102 103 104 105 106 107 108 109) 

特别是,你的功能扩展到类似:

(define (ints-f n) 
    (lazy (eager (cons (delay n) 
        (lazy (ints-f (1+ n))))))) 

,您可以使用使用:

> (define x (ints-f 100)) 
> (force (car (force x))) 
100 
> (force (car (force (cdr (force x))))) 
101 
+0

“膨胀”,或者“如果用'stream-cons'重写,会扩展吗? OP的功能很简单'(cons n(delay ...))',是吗? –

+0

@WillNess'(cons ...(delay ...))'创建奇数流,而'(delay(cons ...))'创建偶数流。奇数流的问题在于你总是物化一个元素太多。使用偶数流,您可以根据需要实现尽可能多的元素。有关更多详细信息,请参阅[SRFI 41]的理论部分(http://srfi.schemers.org/srfi-41/srfi-41.html)。 –

+0

是的,我已经看到了,但是我看到的所有内容都是基于'take'的错误过度实现。 (cf [this](http://stackoverflow.com/questions/38778261/how-to-use-lazy-evaluation-in-guile-scheme/38778442?noredirect=1#comment64969430_38778351))。正确编码很容易,然后异议消失。有一件事情是每个元素都被强制独立的流;另一个是去元素自动强制所有以前的元素;都有自己的位置;我想象后者应该更常用,当然要有正确的“take”。 –