简体   繁体   English

小SICP /计划问题(当地州)

[英]Small SICP/Scheme question (local state)

I'm actually reading the book for fun but it might be considered homework. 我实际上是为了好玩而读这本书,但它可能被认为是家庭作业。 At any event, I don't feel comfortable with local state variables at all with this language... Take for example this code: 无论如何,我对使用这种语言的本地状态变量感到不舒服......以此代码为例:

(define flip
  (let ((count 0))
    (lambda ()
      (if (= 0 count)
          (begin (set! count 1) count)
          (begin (set! count 0) count)))))

why does this code alternate between 1 and 0? 为什么这段代码在1和0之间交替? count is given the value of 0 every time this function is called! 每次调用此函数时,count的值都为0! A python equivalent would be: 一个python等价物将是:

class Flip:
    def __init__(self):
        pass
    def __call__(self):
        count = 0
        if count == 0:
            count = 1
            return count
        else:
            count = 0
            return count

This returns the same thing every time. 每次都会返回相同的内容。 I'm confused... 我糊涂了...

I have a little experience with writing compilers for functional languages, so perhaps a short description of how that function is stored/represented in memory is in order. 我在为函数式语言编写编译器方面有一些经验,所以可能有一个关于如何在内存中存储/表示该函数的简短描述。 Every function can roughly be thought of as a pair (E,F) where E is the set of free variables, and F is the "code" of the function itself. 每个函数大致可以被认为是一对(E,F),其中E是自由变量的集合,F是函数本身的“代码”。 When you call the function, it takes the values in E and substitutes those in for the variables in F, and then executes the code using those values. 当您调用该函数时,它将获取E中的值并将其替换为F中的变量,然后使用这些值执行代码。

So, where your example is concerned, you have defined the variable "flip" to be the function returned by your let expression. 因此,在您的示例中,您已将变量“flip”定义为let表达式返回的函数。 That function is the stuff inside your lambda. 该函数是lambda中的内容。 Because "count" is defined outside the lambda, it's a free variable, so it's stored in the function's environment. 因为“count”是在lambda之外定义的,所以它是一个自由变量,所以它存储在函数的环境中。 Then, every time you call (flip), the interpreter goes to the code in the lambda, sees that it needs to look up the value of "count" in the environment, does that, changes it, and returns. 然后,每次调用(翻转)时,解释器转到lambda中的代码,看到它需要在环境中查找“count”的值,这样做,更改它,然后返回。 That's why each time you call it, the value stored in "count" persists. 这就是为什么每次调用它时,存储在“count”中的值仍然存在。

If you want count to be zero every time you call flip, put the let expression inside the lambda, so it's a bound variable instead of a free variable. 如果你希望每次调用flip时count都为零,那么将let表达式放在lambda中,这样它就是一个绑定变量而不是一个自由变量。

The lambda is a closure. lambda是一个闭包。 It's a function that references a free variable (count), which, not being locally defined or one of the parameters, is bound to the closest enclosing lexical environment. 它是一个引用自由变量(count)的函数,它不是本地定义的,也不是其中一个参数,它绑定在最近的封闭词法环境中。

The function being called is the lambda, not "flip". 被调用的函数是lambda,而不是“flip”。 Flip is just a name you've given to the lambda that's returned out of the (let ...) expression. 翻转只是你给lambda的一个名字,它是从(let ...)表达式返回的。

As for the Python, I don't know the language, but it looks like count should be a member of the Flip object, not a variable local to call . 至于Python,我不知道语言,但看起来count应该是Flip对象的成员,而不是调用本地的变量。

Because your flip function actually returns a function (which is defined inside lambda) 因为你的翻转函数实际上返回一个函数 (在lambda中定义)

Each time you call the returned function it modifies its environment. 每次调用返回的函数时,它都会修改其环境。

If you think about it the let creates the environment (and initializes count to 0) only once - when the lambda function is returned to you. 如果你考虑一下, let会创建环境(并将count初始化为0) - 当lambda函数返回给你时。

In a sense lambda creates a function object for you which uses environment, whose last frame was initialized in let with a single variable count. 在某种意义上,lambda为您创建了一个使用环境的函数对象,其最后一帧在let中使用单个变量计数进行初始化。 Each time you call your function it modifies its environment. 每次调用函数时,它都会修改其环境。 If you call flip a second time it returns another function object with different environment. 如果再次调用flip,它将返回另一个具有不同环境的函数对象。 (count initialized to 0) You can then toggle the two functors independently. (计数初始化为0)然后,您可以单独切换两个仿函数。

If you want to undestand fully how it works you should read about environmantal model . 如果您想完全了解它是如何工作的,您应该阅读有关环境模型的内容

it's more like 它更像是

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

Update with more explanation: The function in Scheme is a closure that "closes" around the free variable count , which is defined in the scope outside it. 更新更多解释: Scheme中的函数是一个闭包,它在自由变量count周围“关闭”,它在它之外的范围内定义。 The way that count is defined in a let with just the function as the body, means that the function is the only thing that can access it -- making count effectively a kind of private mutable state that is attached to the function. 在一个let定义count的方式只有函数作为body,意味着函数是唯一能够访问它的函数 - 使count有效地成为一种附加到函数的私有可变状态。

This is the way "objects" are traditionally created in Scheme in SICP -- to have a let define a bunch of variables (the instance variables, initialized to their initial values), and in the body define a bunch of functions which are "methods" that have shared access to the instance variables. 这就是传统上在SICP中的Scheme中创建“对象”的方式 - let定义一堆变量(实例变量,初始化为它们的初始值),并在body中定义一堆函数,这些是“方法” “具有对实例变量的共享访问权限。 That is why it is natural here to use a Python class to represent what is going on, with count being an instance variable. 这就是为什么在这里使用Python类来表示正在发生的事情是很自然的, count是一个实例变量。

A more literal translation into Python 3.x would be something like this (note that it is only approximate as Python doesn't have a let (limited-scope local variable declaration) syntax, and Python's lambda s can't be used because they don't take statements): 对Python 3.x的更直接的翻译将是这样的(注意它只是近似的,因为Python没有let (有限范围的局部变量声明)语法,并且Python的lambda不能被使用,因为它们不要发表声明):

count = 0

def flip():
    nonlocal count
    if count == 0:
        count = 1
        return count
    else:
        count = 0
        return count

# pretend count isn't in scope after this

The problem with the original code is that it has a strong influence of the imperative style. 原始代码的问题在于它对命令式风格有很强的影响力。 A more idiomatic solution will be: 一个更惯用的解决方案是:

(define (flip)
  (let ((flag #t))
    (lambda ()
      (set! flag (not flag))
      (if flag 1 0))))

To answer the question in you comment ooboo, you want a function that returns a function 要回答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))))

You can trivially change the set! 你可以轻而易举地改变这套! line so that it becomes a counter, not a flipper (more useful). 线,使它成为一个计数器,而不是一个鳍状肢(更有用)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM