我实际上是为了好玩而读这本书,但可能会被视为家庭作业。无论如何,我对使用这种语言的本地状态变量感到不舒服......比如这段代码:
(define flip
(let ((count 0))
(lambda ()
(if (= 0 count)
(begin (set! count 1) count)
(begin (set! count 0) count)))))
为什么这段代码在1和0之间交替?每次调用此函数时,count的值都为0!一个python等价物是:
class Flip:
def __init__(self):
pass
def __call__(self):
count = 0
if count == 0:
count = 1
return count
else:
count = 0
return count
每次都会返回相同的内容。我很困惑......
答案 0 :(得分:4)
我在为函数式语言编写编译器方面有一些经验,所以可能有一个关于如何在内存中存储/表示该函数的简短描述。每个函数大致可以被认为是一对(E,F),其中E是自由变量的集合,F是函数本身的“代码”。当你调用函数时,它接受E中的值并用F代替变量中的值,然后使用这些值执行代码。
因此,在您的示例中,您已将变量“flip”定义为let表达式返回的函数。该函数是lambda中的内容。因为“count”是在lambda之外定义的,所以它是一个自由变量,所以它存储在函数的环境中。然后,每次调用(翻转)时,解释器转到lambda中的代码,看到它需要在环境中查找“count”的值,这样做,更改它,然后返回。这就是为什么每次调用它时,存储在“count”中的值仍然存在。
如果你希望每次调用flip时count都为零,那么将let表达式放在lambda中,这样它就是一个绑定变量而不是一个自由变量。
答案 1 :(得分:3)
lambda是一个闭包。它是一个引用自由变量(count)的函数,它不是本地定义的,也不是其中一个参数,它与最接近的封闭词汇环境绑定。
被调用的函数是lambda,而不是“flip”。翻转只是你给lambda的一个名字,它是从(let ...)表达式返回的。
对于Python,我不知道语言,但看起来count应该是Flip对象的成员,而不是调用的本地变量。
答案 2 :(得分:2)
因为你的翻转函数实际上返回一个函数(在lambda中定义)
每次调用返回的函数时,它都会修改其环境。
如果您考虑一下,让只创建一次环境(并将计数初始化为0) - 当lambda函数返回给您时。
从某种意义上说,lambda为你创建了一个使用环境的函数对象,它的最后一帧在 let 中初始化,只有一个变量count。每次调用函数时,它都会修改其环境。 如果再次调用 flip ,则返回另一个具有不同环境的函数对象。 (计数初始化为0)然后,您可以单独切换两个仿函数。
如果您想完全了解其工作原理,请阅读环境模型。
答案 3 :(得分:2)
更像是
class Flip:
def __init__(self):
self.count = 0
def __call__(self):
if self.count == 0:
self.count = 1
return self.count
else:
self.count = 0
return self.count
更新更多解释
Scheme中的函数是一个闭包,它在自由变量count
周围“关闭”,它在它之外的范围内定义。 count
在let
中定义只有函数作为正文的方式,意味着该函数是唯一可以访问它的函数 - 使count
实际上成为一种私有附加到函数的可变状态。
传统上在SICP中的Scheme中创建“对象”的方式 - 让let
定义一堆变量(实例变量,初始化为它们的初始值),并在body中定义一个一堆函数,它们是对实例变量具有共享访问权限的“方法”。这就是为什么在这里使用Python类来表示正在发生的事情是很自然的,count
是一个实例变量。
对Python 3.x的更直接的翻译将是这样的(注意它只是近似的,因为Python没有let
(有限范围的局部变量声明)语法和Python的{{ 1}} s不能使用,因为它们不接受语句):
lambda
答案 4 :(得分:1)
原始代码的问题在于它对命令式风格有很强的影响力。一个更惯用的解决方案是:
(define (flip)
(let ((flag #t))
(lambda ()
(set! flag (not flag))
(if flag 1 0))))
答案 5 :(得分:0)
要回答ooboo中的问题,你需要一个返回函数的函数
(define make-flipper
(lambda ()
(let ((count 0))
(lambda ()
(let ((v count))
(set! count (- 1 count))
v)))))
;; test it
(let ((flip-1 (make-flipper)))
(format #t "~s~%" (flip-1))
(format #t "~s~%" (flip-1))
(format #t "~s~%" (flip-1))
(let ((flip-2 (make-flipper)))
(format #t "~s~%" (flip-2))
(format #t "~s~%" (flip-2))
(format #t "~s~%" (flip-2))))
您可以轻松更改套装!线,使它成为一个计数器,而不是一个鳍状肢(更有用)。