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

发布于 2024-07-27 14:10:04 字数 602 浏览 2 评论 0原文

我实际上读这本书是为了好玩,但它可能被视为家庭作业。 无论如何,我对这种语言的局部状态变量感到不舒服......以这段代码为例:

(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

每次都会返回相同的内容。 我很困惑...

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? count is given the value of 0 every time this function is called! A python equivalent would be:

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...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

梦里人 2024-08-03 14:10:05

我在为函数式语言编写编译器方面有一些经验,因此也许可以简短地描述一下该函数如何在内存中存储/表示。 每个函数都可以粗略地视为一对 (E,F),其中 E 是自由变量的集合,F 是函数本身的“代码”。 当您调用该函数时,它会采用 E 中的值并将这些值替换为 F 中的变量,然后使用这些值执行代码。

因此,就您的示例而言,您已将变量“flip”定义为 let 表达式返回的函数。 该函数就是 lambda 内部的内容。 因为“count”是在 lambda 外部定义的,所以它是一个自由变量,因此它存储在函数的环境中。 然后,每次调用 (flip) 时,解释器都会转到 lambda 中的代码,发现它需要在环境中查找“count”的值,执行此操作,更改它,然后返回。 这就是为什么每次调用它时,存储在“count”中的值都会保留下来。

如果您希望每次调用 Flip 时 count 都为零,请将 let 表达式放入 lambda 中,这样它就是一个绑定变量而不是自由变量。

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. 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.

So, where your example is concerned, you have defined the variable "flip" to be the function returned by your let expression. That function is the stuff inside your lambda. Because "count" is defined outside the lambda, it's a free variable, so it's stored in the function's environment. 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. That's why each time you call it, the value stored in "count" persists.

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.

吃素的狼 2024-08-03 14:10:05

lambda 是一个闭包。 它是一个引用自由变量(count)的函数,该变量不是本地定义的,也不是参数之一,而是绑定到最近的封闭词法环境。

被调用的函数是 lambda,而不是“flip”。 Flip 只是您为从 (let ...) 表达式返回的 lambda 指定的名称。

至于Python,我不知道该语言,但看起来 count 应该是 Flip 对象的成员,而不是 call 的本地变量。

The lambda is a closure. 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.

The function being called is the lambda, not "flip". Flip is just a name you've given to the lambda that's returned out of the (let ...) expression.

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.

Oo萌小芽oO 2024-08-03 14:10:05

因为你的 Flip 函数实际上返回一个函数(在 lambda 内部定义),

每次调用返回的函数时,它都会修改其环境。

如果您考虑一下,let 仅创建一次环境(并将 count 初始化为 0) - 当 lambda 函数返回给您时。

从某种意义上说,lambda 为您创建了一个使用环境的函数对象,其最后一帧在 let 中使用单个变量 count 进行了初始化。 每次调用函数时,它都会修改其环境。
如果您第二次调用flip,它会返回另一个具有不同环境的函数对象。 (计数初始化为 0)然后您可以独立切换两个函子。

如果您想完全理解它是如何工作的,您应该阅读环境模型

Because your flip function actually returns a function (which is defined inside 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.

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. 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. (count initialized to 0) You can then toggle the two functors independently.

If you want to undestand fully how it works you should read about environmantal model.

混浊又暗下来 2024-08-03 14:10:05

它更像是

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 进行“关闭”的闭包,该变量是在其外部的作用域中定义的。 countlet 中定义的方式仅将函数作为主体,这意味着该函数是唯一可以访问它的东西 - 使得 count 实际上是一种附加到函数的私有可变状态。

这是在 SICP 中的方案中传统上创建“对象”的方式——让 let 定义一堆变量(实例变量,初始化为其初始值),并在主体中定义一个一堆函数,它们是共享实例变量的“方法”。 这就是为什么这里很自然地使用 Python 类来表示正在发生的事情,并使用 count 作为实例变量。

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

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. 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.

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. That is why it is natural here to use a Python class to represent what is going on, with count being an instance variable.

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 lambdas can't be used because they don't take statements):

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
皓月长歌 2024-08-03 14:10:05

原始代码的问题在于它受到命令式风格的强烈影响。 更惯用的解决方案是:

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

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))))
记忆で 2024-08-03 14:10:05

要回答您评论 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))))

您可以轻松更改该集合! 线,使其成为计数器,而不是鳍状肢(更有用)。

To answer the question in you comment ooboo, you want a function that returns a function

(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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文