Lambda 用“定义多个本地函数”;“让开兰姆达”;计划中的表格
我对在Scheme中定义多个可以相互调用的词汇范围函数感到好奇。在使用SICP时,我使用块结构生成了以下函数来解决练习1.8(使用牛顿法计算立方根): 下面我看到的问题是当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
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组合器,但它相当复杂且效率低下。)
幸运的是,所有这些秘密都将在第三章中披露