为什么在函数定义中进行自调用是合法的,但对于值是非法的?

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

Structure and Interpretation of Computer Programs (SICP) 3.5.2 引入无限流:

(define ones
  (cons-stream 1 ones))

此代码在 DrRacket 中无效,错误为:

ones: undefined; cannot reference an identifier before its definition

像这样的其他代码:

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

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

产生错误:

Interactions disabled

(陷入死循环?)

据我阅读(SICP),实现无限流的关键点是延迟评估:

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

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

cons-stream中使用这个,无限流仍然是非法的。

这样的数据结构让我想起了递归函数,在其定义中自调用在实际退出或没有实际退出时都是合法的(在编译中)。

为什么值引用自身是非法的?连引用都延迟了?

其他编程语言可以支持无限流吗?

如果不是,是关于处理器处理汇编语言的方式吗?数据栈的东西?

这是因为define 中的表达式在 名称绑定到一个值之前被求值。它尝试在定义 ones 之前评估 (cons-stream 1 ones),从而导致错误。

这对函数工作良好的原因是 函数的主体没有被计算 函数被计算时。也就是说,要评估 (lambda (x) (f x)),语言 returns 一个函数而不看它的主体。自

(define (f x) (f x))

是定义lambda的语法糖,逻辑相同。

你自己定义cons-stream了吗?如果您将 cons-stream 设为普通函数,它将无法正常工作。由于 Scheme 在默认情况下是严格的,因此在调用函数 之前 评估参数。如果 cons-stream 是一个普通函数,b 将在传递给 delay 之前得到完整的计算,使你陷入无限循环。

cons-stream 在 SICP 中是一个 "special form" 而不是函数,这意味着它可以控制如何计算其参数。

如果您使用 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 不会做任何事情,因为它已经在那个时候计算过了。 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中的默认语言,#!rackethas streams too,只是名字不同:

#!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 版本中的尾部。

除了上面的答案之外,为了确保代码 运行,您还应该像这样定义 delay

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

delay 以及 cons-stream 必须定义为宏。

在另一种选择中,您可以只调用 Racket 中预定义的 delay 而不是构建新的。

但不要这样做:

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

编译器会采用您的定义,因此程序在求值时崩溃:

Interactions disabled