Sof*_*mur 6 scheme closures scope racket
我看到以下代码......第一次调用(next-num)return 1,第二次返回2.
(define next-num
(let ((num 0))
(lambda () (set! num (+ num 1)) num)))
(next-num) ; 1
(next-num) ; 2
Run Code Online (Sandbox Code Playgroud)
我无法理解的是... num是由let内部创建的next-num,它是一种局部变量...方案如何知道每次next-num被调用,值num都没有被删除let ((num 0)); 方案如何知道num每当next-num调用它时它总是相同的?
它似乎num是本地的和静态的......我们如何定义局部变量,而不是静态变量?
这是"词法闭包"而你是对的num,"闭合变量"类似于静态变量,例如在C中:它只对let表单中的代码可见(它的"词法范围"),但它贯穿整个程序运行,而不是每次调用函数重新初始化.
我认为你所困惑的部分是:" num是通过内部创建的next-num,它是一种局部变量".这不是真的,因为let块不是next-num函数的一部分:它实际上是一个表达式,它创建并返回随后绑定的函数next-num.(这是非常不同的,例如,来自C,其中函数只能在编译时创建并通过在顶层定义它们.在Scheme中,函数是整数或列表之类的值,任何表达式都可以返回).
这是另一种写(几乎)相同的东西的方式,这使得更清楚的define是它只是与next-num函数返回表达式的值相关联:
(define next-num #f) ; dummy value
(let ((num 0))
(set! next-num
(lambda () (set! num (+ num 1)) num)))
Run Code Online (Sandbox Code Playgroud)
重要的是要注意它们之间的区别
(define (some-var args ...) expression expression ...)
Run Code Online (Sandbox Code Playgroud)
这使得some-var一个函数expressions在被调用时执行所有操作,并且
(define some-var expression)
Run Code Online (Sandbox Code Playgroud)
它与当时和那里some-var的价值相关联expression.严格来说,前一版本是不必要的,因为它相当于
(define some-var
(lambda (args ...) expression expression ...))
Run Code Online (Sandbox Code Playgroud)
您的代码几乎与此相同,并num在lambda表单周围添加了词法范围的变量.
最后,这是封闭变量和静态变量之间的关键区别,这使得闭包更加强大.如果你写了以下内容:
(define make-next-num
(lambda (num)
(lambda () (set! num (+ num 1)) num)))
Run Code Online (Sandbox Code Playgroud)
然后每次调用make-next-num都会创建一个匿名函数,该函数带有一个新的,不同的num变量,该变量对该函数是私有的:
(define f (make-next-num 7))
(define g (make-next-num 2))
(f) ; => 8
(g) ; => 3
(f) ; => 9
Run Code Online (Sandbox Code Playgroud)
这是一个非常酷且强大的技巧,它解释了词汇封闭语言的很多功能.
编辑添加:您询问Scheme如何"知道" num在next-num调用时修改哪个.概括地说,如果没有实现,这实际上非常简单.Scheme中的每个表达式都在变量绑定的环境(查找表)的上下文中进行评估,变量绑定是名称与可以保存值的位置的关联.对let表单或函数调用的每个评估都通过使用新绑定扩展当前环境来创建新环境.为了使lambda表单表现为闭包,实现将它们表示为由函数本身和定义它的环境组成的结构.然后通过扩展定义函数的绑定环境来评估对该函数的调用 - 而不是调用它的环境.
较旧的Lisp(包括直到最近的Emacs Lisp)有lambda,但不是词法范围,所以尽管你可以创建匿名函数,但是对它们的调用将在调用环境而不是定义环境中进行评估,因此没有闭包.我相信Scheme是第一种能够做到这一点的语言.Sussman和Steele 关于Scheme实施的原始Lambda论文为任何想要了解范围界定的人提供了极大的思维扩展阅读.