使用 lambda 模拟银行余额提取行为

Simulate a bank balance withdraw behavior with lambda

我正在阅读3.1 Assignment and Local State of SICP

(define balance 100)
(define (withdraw amount)
  (if (>= balance amount)
      (begin (set! balance (- balance amount))
             balance) ;
      "Insufficient funds"))
;Value: balance

1 ]=> 
;Value: withdraw
1 ]=> (withdraw 50)
;Value: 50
1 ]=> (withdraw 30)
;Value: 20

该方案存在暴露balance状态的问题,作为解决方案,引入局部变量

(define new-withdraw
  (let ((balance 100))
    (lambda (amount) ;;
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds"))))

> (new-withdraw 40)
;Value: 60

lambda也被引入,甚至认为它可以改写为

(define (new-withdraw-2 amount)
  (let ((balance 100))
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds")))

;Value: new-withdraw-2
8 error> (new-withdraw-2 30)
;Value: 70

我认为在这里应用 lambda 似乎没有必要。我错过了什么?

第三个函数与第二个函数非常不同,因为在其中每次取款时都会计算结果值 100 - amount(函数的参数),而在第二个函数中有初始余额 100,每次您提款时,都会从 当前 余额中扣除。

(define new-withdraw
  (let ((balance 100))
    (lambda (amount) ;;
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds"))))

> (new-withdraw 40)
;Value: 60

> (new-withdraw 30)
;Value: 30           ; equal to 60 - 30


(define (new-withdraw-2 amount)
  (let ((balance 100))
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds")))

> (new-withdraw-2 40)
;Value: 60

>(new-withdraw-2 30)
;Value: 70   ; equal to 100 - 30

因此,new-withdraw-2 不模拟银行账户,函数只是定义 f (x) = 100 - x 的一种复杂方式。

为什么这两个功能差异如此之大,即使表面上看起来很相似?

差异由let的语义给出:在第一种情况下,在new-withdrawlet引入了一个新变量balance,它建立了一个新的“环境”,然后 return 一个新函数(内部 lambda),变量 balance 是外部的。因此,每次调用 returned 函数时,都会访问 same 变量并减少其值。下次调用时,会发现,通过same变量,上次调用时的值变小了。 函数内部等对象被称为“闭包”,因为函数与外部环境是严格相连的,环境在函数中形成一种“隐藏”的状态,并在函数的不同调用之间持久存在。

在第二种情况下,let 位于函数 new-withdraw-2 内:这意味着每次调用该函数时,一个 new变量 balance 被定义,它建立了一个新的环境,局部于函数,变量被初始化为 100,然后它被减少(set!)。但是当函数终止,returning new balance,函数的本地环境就丢失了,下次调用函数时会重新建立新的环境,变量balance 再次初始化为 100.