首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么在函数定义中进行自我调用是合法的,但对于价值却是非法的呢?

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

Stack Overflow用户
提问于 2015-08-08 16:28:34
回答 3查看 204关注 0票数 4

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

代码语言:javascript
运行
复制
(define ones
  (cons-stream 1 ones))

此代码在DrRacket中不起作用,错误如下:

1:未定义;不能在定义之前引用标识符。

其他代码如下:

代码语言:javascript
运行
复制
(define (integers-starting-from n)
  (cons-stream n
               (integers-starting-from (+ n 1))))

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

产生错误:

交互禁用

(陷入无限循环?)

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

代码语言:javascript
运行
复制
(define (delay exp)
  (lambda () exp))

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

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

这种数据结构让我想起了递归函数,在它的定义中,自调用是合法的(在编译中),无论是在实际的退出中还是在不存在的情况下。

为什么引用自己的值是非法的?连推荐信都被推迟了?

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

如果不是,这是处理器处理汇编语言的方式吗?数据堆栈之类的?

EN

回答 3

Stack Overflow用户

发布于 2015-08-08 16:47:47

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

这对于函数来说工作得很好的原因是,当函数被计算时,不对函数的主体进行计算。也就是说,要计算(lambda (x) (f x)),语言返回一个函数,而不查看它的主体。因为

代码语言:javascript
运行
复制
(define (f x) (f x))

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

您是否将cons-stream定义为上面所示?如果您使cons-stream成为一个正常的函数,它将不能正常工作。由于Scheme在默认情况下是严格的,因此在调用函数之前对参数进行计算。如果cons-stream是一个正常函数,那么在传递给delay之前,b将被完全计算,这就使您遇到了一个无限循环。

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

如果您使用stream-cons和其他内置在Racket中的stream-操作,您将得到您想要的行为。

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

代码语言:javascript
运行
复制
ones = 1 : ones
票数 2
EN

Stack Overflow用户

发布于 2015-08-08 16:50:06

这一定义适用于球拍:

代码语言:javascript
运行
复制
(define ones
  (cons-stream 1 ones))

…只要您将cons-stream的延迟实现作为一种特殊形式提供,这就是SICP第3.5节的全部要点:

代码语言:javascript
运行
复制
(define-syntax cons-stream
  (syntax-rules ()
    ((_ head tail)
     (cons head (delay tail)))))
票数 1
EN

Stack Overflow用户

发布于 2015-08-09 16:31:45

添加到上面的答案中,为了确保代码运行,您还应该像下面这样定义delay

代码语言:javascript
运行
复制
(define-syntax delay
  (syntax-rules ()
    ((_ exp)
     (lambda () exp))))

必须将delaycons-stream定义为宏。

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

但别这么做:

代码语言:javascript
运行
复制
(define (delay exp)
  (lambda () exp))

编译器将接受您的定义,因此程序在计算中崩溃:

交互禁用

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31896117

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档