Chapter3.1 高階手続きによる抽象

...超久しぶりに、lisp をいじってみたくなったので、SICPを読みながら gauche をいじる.

本日は、Chapter3.1 のあたりを読みながらlambda 式について学んでみた.

lambda 式って言葉は聞いたことはあるけれども、全然どんなものかわからなかったんだけれども、今回実際に手を動かしてみてなんとなくわかってきた気がする...

要するに、無名の関数ってところなのね

(SICPの例のままだけれども)次のような式があったとする

f(x, y) = x(1 + xy)^2 + y(1 - y) + (1 + xy)(1 - y)

この式を計算する場合には、

a = 1 + xy

b = 1 - y

とおいて、

f(x, y) = xa^2 + yb + ab

の計算をすればよい.

さて、lambdaを利用せずに関数定義だけでこれを行おうとすると次のようになる.

(define (f x y)
  (define (square a) (* a a))
  (define (f-helper a b)
    (+ (* x (square a))
       (* y b)
       (* a b)))
  (f-helper (+ 1 (* x y))
	    (- 1 y)))

手始めに、このコードの二乗部分をlambda式に置き換えてみると次のようになる

;; 2乗部分をlamda を利用して書き直してみる
(define (lambda-f x y)
  (define (f-helper a b)
    (+ (* x ((lambda (c) (* c c)) a))
       (* y b)
       (* a b)))
  (f-helper (+ 1 (* x y))
	    (- 1 y)))

肝になっている部分は

((lambda (c) (* c c)) a)

の部分で、二乗を行うための lambda 式に、親の関数(f-helper)の引数である a を渡して a^2 を計算している.

もう一つぐらい例示することにして、今度は、f-helper 自身をlambda 式として扱ってみる.
f-helper は、関数 lambda-f の内部でしか利用されてい局所てきな関数なので、わざわざ f-helpet として名前をつける必要もないだろう

(define (lambda-f1 x y)
  ((lambda (a b)
     (+ (* x (square a))
	(* y b)
	(* a b)))
   (+ 1 (* x y))
   (- 1 y)))

構文として書き起こしてみるとこんな感じ

((lambda (<引数1> <引数2> ... <引数n>)
<本体>)
<式に渡す引数1>
<式に渡す引数2>
...
<式に渡す引数n>)

で、lambda は利用用途が高い(らしい)ので、特殊系が let という特殊形式が存在している.

先ほどの式を、let を利用して書き直してみると、次のようになる

;; let を利用した簡略形
(define (let-f x y)
  (let ((a (+ 1 (* x y)))
	(b (- 1 y)))
    (+ (* x (square a))
       (* y b)
       (* a b))))

SICPでは、「まぁシンタックスシュガーだからわかるよね?」程度に書かれているけれども、私はようわからんかった

lambdaの構文で利用した言葉を使って、letの構文に置き換えてみると

(let *1
<本体>)

と、なる.

...おぉ、確かにシンタックスシュガーだわ.

なんとなく、lambda とか let についてわかった気がします.

追記:
正直、lisp が役に立つのかとか、強力だとかはわからないけれども、今までのプログラム脳(?)とは違う場所を使っている気がして結構楽しいかも...

*1:<引数1> <式に渡す引数1>) (<引数2> <式に渡す引数2>) .... (<引数n> <式に渡す引数n>