Имитация поведения снятия баланса с помощью лямбды - PullRequest
0 голосов
/ 02 ноября 2019

Я читаю 3.1 Назначение и локальное состояние 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 здесь, кажется, не нужно. Что я пропустил?

1 Ответ

1 голос
/ 02 ноября 2019

Третья функция сильно отличается от второй, так как в ней каждый раз, когда вы делаете вывод, вы вычисляете как значение результата 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-withdraw let вводит новую переменную balance, которая устанавливает новую «среду», а затем возвращает новую функцию (внутреннюю lambda), для которой переменнаяbalance является внешним. Таким образом, возвращаемая функция, каждый раз, когда она вызывается, обращается к той же самой переменной и уменьшает ее значение. В следующий раз, когда он будет вызван, через ту же самую переменную будет найдено значение, уменьшенное в предыдущем вызове. Такие объекты, как внутренняя функция, называются «замыканиями», поскольку функция и внешняя среда жестко связаны со средой, образующей «скрытое» в функции состояние и сохраняющейся между различными вызовами функции.

Во втором случае вместо этого let находится внутри функции new-withdraw-2: это означает, что каждый раз, когда вызывается функция, определяется new переменная balance, которая устанавливает новыйВ среде, локальной для функции, переменная инициализируется до 100, а затем уменьшается (с set!). Но когда функция завершается, возвращая новый баланс, локальное окружение функции теряется, и в следующий раз, когда функция будет вызвана, новое окружение будет установлено снова, при этом переменная balance снова инициализируется равной 100.

...