Functional programming 为什么在函数定义中进行自调用是合法的,而在值中是非法的?

Functional programming 为什么在函数定义中进行自调用是合法的,而在值中是非法的?,functional-programming,scheme,racket,sicp,Functional Programming,Scheme,Racket,Sicp,计算机程序的结构和解释(SICP)3.5.2引入了无限流: (define ones (cons-stream 1 ones)) ones = 1 : ones 此代码在DrRacket中不起作用,错误如下: 一:未定义;无法在标识符定义之前引用标识符 其他类似代码: (define (integers-starting-from n) (cons-stream n (integers-starting-from (+ n 1)))) (define

计算机程序的结构和解释(SICP)3.5.2引入了无限流:

(define ones
  (cons-stream 1 ones))
ones = 1 : ones
此代码在DrRacket中不起作用,错误如下:

一:未定义;无法在标识符定义之前引用标识符

其他类似代码:

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

(define integers (integers-starting-from 1))
(define-syntax delay
  (syntax-rules ()
    ((_ exp)
     (lambda () exp))))
产生错误:

禁用交互

(落入无限循环?)

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

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

(define (cons-stream a b)
  (cons a
        (delay b)))
cons stream
中使用此选项后,无限流仍然是非法的

这种数据结构让我想起了递归函数,在它的定义中,自调用(在编译中)在实际出口之内或之外都是合法的

为什么值引用自身是非法的?甚至参考资料也被推迟了

其他编程语言能支持无限流吗


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

这是因为定义中的表达式是在名称绑定到值之前计算的。它试图在定义
个数之前计算
(cons stream 1个数)
,从而导致错误

这对于函数来说效果很好的原因是,当函数被调用时,函数体不会被求值。也就是说,为了计算
(lambda(x)(fx))
,该语言返回一个函数,而不查看函数体。自

(define (f x) (f x))
是定义lambda的语法,同样的逻辑也适用

您是否像上面那样定义了
cons-stream
?如果将
cons-stream
设置为正常功能,它将无法正常工作。由于Scheme在默认情况下是严格的,所以在调用函数之前会计算参数。如果
cons stream
是一个正常函数,
b
将在传递到
delay
之前得到完整的评估,从而使您进入无限循环

cons-stream
在SICP中是一种“特殊形式”而不是一个函数,这意味着它可以控制参数的求值方式

如果您使用tracket内置的
流cons
和其他
流-
操作,您将获得想要的行为

最后,其他一些语言确实允许值引用自己。Haskell就是一个很好的例子,在这里它可以工作,因为默认情况下一切都是懒惰的。下面是一个Haskell片段,它将
one
定义为一个无限的one列表。(由于一切都是惰性的,Haskell列表的行为就像Scheme streams):


此定义适用于球拍:

(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流
必须是宏

DrRacket不是一种语言实现。它是一个支持多种语言的IDE。其中之一是SICP兼容语言。我想在DrRacket中使用此代码运行您的代码而不出错:

#!尼尔星球/sicp
(定义
(一人)
(定义(从n开始的整数)
(反对意见)
(从(+n1)开始的整数)
(定义整数(从1开始的整数))
它就像一个符咒。DrRacket中的默认语言,
#!球拍
,但名称不同:

#!球拍
(定义
(1个)
(定义(从n开始的整数)
(溪流)
(从(+n1)开始的整数)
(定义整数(从1开始的整数))
然而,对于方案,您应该使用SRFI-41,您可以从两个
#使用它!球拍
(使用
(需要srfi/41)
)和
#!r6rs
(完成后R7RS变大)

(导入(rnrs)
(srfi:41))
(定义
(1个)
(定义(从n开始的整数)
(溪流)
(从(+n1)开始的整数)
(定义整数(从1开始的整数))
在两个
#中滚动您自己的SICP流!球拍
#!r6rs
您可以使用
定义语法

(定义语法流)
(语法规则()
((a d)(cons a(delay d))))

请注意,RSFI-41和rackets拥有
#的流库!racket
延迟
流cons
中的两个值,而不仅仅是SICP版本中的尾部。

除了上面的答案,为了确保代码运行,您还应该定义
延迟
,如下所示:

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

(define integers (integers-starting-from 1))
(define-syntax delay
  (syntax-rules ()
    ((_ exp)
     (lambda () exp))))
delay
以及
cons-stream
必须定义为宏

在另一个选项中,您可以调用Racket中预定义的
延迟
,而不是构建一个新的延迟

但不要这样做:

(define (delay exp)
  (lambda () exp))
编译器将接受您的定义,因此程序在计算时崩溃:

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

(define (cons-stream a b)
  (cons a
        (delay b)))
禁用交互


又见:哦,我明白了。
特殊表单
,就像练习1.6中的
新if
错误一样。Racket已经为那些
自定义
函数设置了边界。这是
尾部递归
?@Rahn这是一个宏,不是尾部递归过程。