Lambda 用“定义多个本地函数”;“让开兰姆达”;计划中的表格

Lambda 用“定义多个本地函数”;“让开兰姆达”;计划中的表格,lambda,scheme,sicp,lexical-scope,Lambda,Scheme,Sicp,Lexical Scope,我对在Scheme中定义多个可以相互调用的词汇范围函数感到好奇。在使用SICP时,我使用块结构生成了以下函数来解决练习1.8(使用牛顿法计算立方根): 下面我看到的问题是当cbrt-iter试图调用足够好?过程时。由于足够好吗?过程仅在第一个嵌套的let块的范围内,因此cbrt iter无法访问它。似乎可以通过将cbrt iter函数嵌套在的let中来解决这一问题,但这似乎也非常笨拙 在这种情况下,define表单做了哪些不同的工作?define表单是否扩展为lambda表达式而不是“let o

我对在Scheme中定义多个可以相互调用的词汇范围函数感到好奇。在使用SICP时,我使用块结构生成了以下函数来解决练习1.8(使用牛顿法计算立方根):

下面我看到的问题是当
cbrt-iter
试图调用
足够好?
过程时。由于
足够好吗?
过程仅在第一个嵌套的
let
块的范围内,因此
cbrt iter
无法访问它。似乎可以通过将
cbrt iter
函数嵌套在
let
中来解决这一问题,但这似乎也非常笨拙


在这种情况下,
define
表单做了哪些不同的工作?
define
表单是否扩展为
lambda
表达式而不是“let over lambda”表单(我记得在《小阴谋家》一书中使用表单
((lambda(x)x)(lambda(y)…)
)做了类似的事情,但我不确定这是如何工作的)。另外,通过比较,Common Lisp是如何处理这种情况的?是否可以使用字典范围的
defun

首先,您不需要引入新的过程
calc cbrt
——您只需调用
calc iter

其次,
define
let
的含义完全不同<代码>定义
将定义安装到本地范围,如您的示例所示。然而,
let
表达式只是
lambda
表达式的语法糖(有关详细信息,请参见SICP第1.3节)。因此(正如您所提到的),通过
(let(…)
声明的变量仅在
内部可见。因此,您的
(let)(let)…
模式不起作用,因为没有一个定义会在其他范围中“存活”下来

所以,我们应该这样写:

(define (cbrt x)
   (let ((good-enough? (lambda ...))
         (improve (lambda ...))
         (cbrt-iter (lambda ...)))
     (cbrt-iter 1.0 0.0)))
现在,至少调用
cbrt-iter
可以看到
cbrt-iter
的定义

但还是有一个问题。当我们评估
(cbrt iter 1.0 0.0)
时,我们评估
cbrt iter的主体,其中
guess
prev guess
取值1.0和0.0。但是,在
cbrt-iter
的主体中,变量
改善
足够好?
不在范围之内

您可能会尝试使用嵌套的
let
s,这通常是一个不错的选择:

(define (cbrt x)
   (let ((good-enough? (lambda ...))
         (improve (lambda ...)))
     (let ((cbrt-iter (lambda ...)))
       (cbrt-iter 1.0 0.0))))
问题是cbrt iter需要调用自身,但它不在范围内,直到内部
let
的主体

这里的解决方案是使用
letrec
,这类似于
let
,但使新绑定在所有声明以及主体中都可见:

(define (cbrt x)
   (let ((good-enough? (lambda ...))
         (improve (lambda ...)))
     (letrec ((cbrt-iter (lambda ...)))
       (cbrt-iter 1.0 0.0))))
我们甚至可以使用
letrec
创建相互递归的过程,就像使用
define
一样

不幸的是,我需要一些时间来解释
letrec
define
实际上是如何工作的,但这里有一个秘密:它们都在内部使用变异来创建环境数据结构中的循环,允许递归。(也有一种只使用
lambda
创建递归的方法,称为Y组合器,但它相当复杂且效率低下。)

幸运的是,所有这些秘密都将在第三章和第四章中披露


从另一个角度来看,您可以看一看,这基本上直接涉及到这个主题(虽然它省略了定义),但我发现SICP更能迫使您理解创建的有时复杂的环境结构。

首先,您不需要引入新的过程
calc cbrt
——您可以调用
calc iter

其次,
define
let
的含义完全不同<代码>定义
将定义安装到本地范围,如您的示例所示。然而,
let
表达式只是
lambda
表达式的语法糖(有关详细信息,请参见SICP第1.3节)。因此(正如您所提到的),通过
(let(…)
声明的变量仅在
内部可见。因此,您的
(let)(let)…
模式不起作用,因为没有一个定义会在其他范围中“存活”下来

所以,我们应该这样写:

(define (cbrt x)
   (let ((good-enough? (lambda ...))
         (improve (lambda ...))
         (cbrt-iter (lambda ...)))
     (cbrt-iter 1.0 0.0)))
现在,至少调用
cbrt-iter
可以看到
cbrt-iter
的定义

但还是有一个问题。当我们评估
(cbrt iter 1.0 0.0)
时,我们评估
cbrt iter的主体,其中
guess
prev guess
取值1.0和0.0。但是,在
cbrt-iter
的主体中,变量
改善
足够好?
不在范围之内

您可能会尝试使用嵌套的
let
s,这通常是一个不错的选择:

(define (cbrt x)
   (let ((good-enough? (lambda ...))
         (improve (lambda ...)))
     (let ((cbrt-iter (lambda ...)))
       (cbrt-iter 1.0 0.0))))
问题是cbrt iter需要调用自身,但它不在范围内,直到内部
let
的主体

这里的解决方案是使用
letrec
,这类似于
let
,但使新绑定在所有声明以及主体中都可见:

(define (cbrt x)
   (let ((good-enough? (lambda ...))
         (improve (lambda ...)))
     (letrec ((cbrt-iter (lambda ...)))
       (cbrt-iter 1.0 0.0))))
我们甚至可以使用
letrec
创建相互递归的过程,就像使用
define
一样

不幸的是,我需要一些时间来解释
letrec
define
实际上是如何工作的,但这里有一个秘密:它们都在内部使用变异来创建环境数据结构中的循环,允许递归。(也有一种只使用
lambda
创建递归的方法,称为Y组合器,但它相当复杂且效率低下。)

幸运的是,所有这些秘密都将在第三章中披露