簡體   English   中英

為什么在函數定義中合法進行自我調用但對於值是非法的?

[英]Why is it legal in a function definition to make self-call but illegal for a value?

計算機程序的結構和解釋(SICP)3.5.2引入了無限流:

(define ones
  (cons-stream 1 ones))

此代碼在DrRacket中不起作用,錯誤如下:

一個:未定義; 在定義之前不能引用標識符

其他代碼如下:

(define (integers-starting-from n)
  (cons-stream n
               (integers-starting-from (+ n 1))))

(define integers (integers-starting-from 1))

產生錯誤:

交互禁用

(陷入無限循環?)

據我所知(SICP),實現無限流的關鍵是延遲評估:

(define (delay exp)
  (lambda () exp))

(define (cons-stream a b)
  (cons a
        (delay b)))

有了這個用於cons-stream ,無限流仍然是非法的。

這樣的數據結構讓我想起了遞歸函數,在其定義中,自調用是合法的(在編譯中)是否在實際退出之內。

為什么值引用自身是非法的? 連參考都被推遲了?

其他編程語言可以支持無限流嗎?

如果沒有,它是關於處理器處理匯編語言的方式嗎? 數據堆棧的東西?

在創建過程時,在啟動過程主體時已經評估了參數。 因此delay不會做任何事情,因為它已經在那時計算過了。 cons-stream需要是一個宏。

DrRacket不是一種語言實現。 它是一個支持許多語言的IDE。 其中之一是SICP兼容性語言。 我使用DrRacket中的代碼管理您的代碼而不會出錯:

#!planet neil/sicp

(define ones
  (cons-stream 1 ones))

(define (integers-starting-from n)
  (cons-stream n
               (integers-starting-from (+ n 1))))

(define integers (integers-starting-from 1))

它就像一個魅力。 DrRacket中的默認語言#!racket 也有流 ,但名稱不同:

#!racket 

(define ones
  (stream-cons 1 ones))

(define (integers-starting-from n)
  (stream-cons n
               (integers-starting-from (+ n 1))))

(define integers (integers-starting-from 1))

但是對於Scheme你應該使用SRFI-41,你可以使用#!racket (使用(require srfi/41) )和#!r6rs (和R7RS-large完成時)

(import (rnrs)
        (srfi :41))

(define ones
  (stream-cons 1 ones))

(define (integers-starting-from n)
  (stream-cons n
               (integers-starting-from (+ n 1))))

(define integers (integers-starting-from 1))

要在#!racket#!r6rs滾動自己的SICP流,可以使用define-syntax

(define-syntax stream-cons 
  (syntax-rules ()
    ((_ a d) (cons a (delay d)))))

請注意,RSFI-41和球拍擁有#!racket的流庫會延遲stream-cons兩個值,而不僅僅是SICP版本中的尾部。

這是因為在名稱綁定到值之前會計算define的表達式。 它試圖評估(cons-stream 1 ones)之前ones被定義,從而引起錯誤。

這對函數來說效果很好的原因是函數的主體在函數時不被計算 也就是說,為了評估(lambda (x) (fx)) ,語言返回一個函數而不查看它的主體。 以來

(define (f x) (f x))

是用於定義lambda的語法糖,同樣的邏輯適用。

您是否按上述方式自定義了cons-stream 如果你使cons-stream成為正常函數,它將無法正常工作。 由於Scheme默認是嚴格的,因此調用函數之前會計算參數。 如果cons-stream是一個普通函數, b在被傳遞給delay之前會被完全評估,從而使你無限循環。

SICP中的cons-stream是一種“特殊形式”而不是函數,這意味着它可以控制其參數的評估方式。

如果您使用內置於Racket中的stream-cons和其他stream-操作,您將獲得所需的行為。

最后,一些其他語言確實允許值參考自己。 一個很好的例子是Haskell,這是有效的,因為默認情況下一切都是懶惰的 這里有一個Haskell的片段定義ones是那些無限名單。 (因為一切都很懶惰,Haskell列表就像Scheme流一樣):

ones = 1 : ones

這個定義適用於Racket:

(define ones
  (cons-stream 1 ones))

...只要您提供延遲實施的cons-stream作為特殊形式,這是SICP第3.5節的全部要點:

(define-syntax cons-stream
  (syntax-rules ()
    ((_ head tail)
     (cons head (delay tail)))))

添加上面的答案,為了確保代碼運行,您還應該像這樣定義delay

(define-syntax delay
  (syntax-rules ()
    ((_ exp)
     (lambda () exp))))

delay以及cons-stream必須定義為宏。

在另一個選項中,您可以調用在Racket中預定義的delay而不是構建新的delay

但是不要這樣做:

(define (delay exp)
  (lambda () exp))

編譯器將采用您的定義,因此程序在評估中崩潰:

交互禁用

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM