2016-08-04 67 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」。 –