さくらんぼのlambda日記

lambdaちっくなことからゲーム開発までいろいろ書きます。

クロージャってなんだろう

某所でSICPのことを聞かれて、クロージャとは何者かと聞かれて
概要は説明できたが非常に機微な部分の説明が怪しかったので
自分への復習の意味も兼ねてクロージャに再入門してみます。

まず、クロージャというのは簡単にいうと、

  1. 関数が定義されたときの環境
  2. その関数自体

がセットになったデータ構造です。

今回扱う言語はSICPでも扱っているLispの方言
schemeです。

common lispとの違いはいろいろありますが、とりあえずは
defun -> defineになっているくらいで取り合えず今回の内容は理解できるはず。

まず、クロージャを作る方法
これは簡単です。

(lambda (x) x)

lambdaで囲われた世界がクロージャです。

とここまでは簡単なのですが
SICPで扱っている例は若干複雑です。

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

(define (make-withdraw balance)
  (lambda (amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds")))

こういう定義がされている場合に、ほとんど同じように見えるのですが
大きく違います。

(define W1 (make-withdraw 100))
(define W2 (make-withdraw 100))
(W1 50)
50
(W2 70)
30
(W2 40)
"Insufficient funds"
(W1 40)
10

このように、make-withdrawbalanceでは、実行するたび新しいクロージャを作って返してくれます。
しかし、new-withdrawではそのようにはなりません。

(new-withdraw 10)
90
(new-withdraw 20)
70

このように同じクロージャをnew-withdrawでは返します。

これは何が違ってこのようになったのか、よく考えないと分かりません。
ここでdefineおよびletをlambda式を使った同一の式にすると理解の助けになると思います。

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

(define make-withdraw 
(lambda (balance)
  (lambda (amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds"))))

このようにしてみると、両者の違いが歴然としてきます。



(define new-withdraw .......)では
*1がmake-withdrawに束縛されています。
これは引数をひとつ必要とするlambda式で(make-withdraw 100)などとしてやっと始めて
クロージャが実際に作られるものです。
なので呼ばれるたびに違うクロージャが作られるという結果になります。



SICPは面白い本なのですが、内容が濃くなってて一文も見逃さないように
本当に注意深く読まないといけないですね。

ちゃんちゃん。

*1:lambda (balance) (......) 100)と定義されているクロージャに new-withdrawが束縛されています。 ですので、呼ばれるたびにまったく同じ場所を参照するわけです。 (define make-withdraw....)では (lambda (balance) (....)