値としてのラムダ式


カリー(curry)化 *

    (lambda (x y) expression)
というラムダ式は、 x y の組を引数としてもらい、expression の評価値を返す関数です。 これと似たラムダ式として、
    (lambda (x) (lambda (y) expression)) 
というものがあります。 これは、 x を引数として渡し、その結果に更に y を引数として渡すと、 expression の評価値を返します。 実際の例で比べると以下のようになっています。

> ((lambda (x y) (+ x y)) 3 4)
7
> (((lambda (x) (lambda (y) (+ x y))) 3) 4)
7
前者を後者の様に変換することをカリー化する(currying)、 その逆をアンカリー化する(uncarrying)といいます。 二つのラムダ式の違いは、前者が引数が揃った時点で動き始めるのに対して、 後者は、まず、 x が渡された時点で、 y を引数としたラムダ式を返し、 その後、引数 y が渡されて評価が進む点です。 ちなみに、 カリー化自身も関数として定義できます(練習問題)。

こんなことをしてなにが嬉しいのでしょう? 例えば、n m 乗を計算する power を次の様に定義したとします。

        (define (power m n) (if (= m 0) 1 (* n (power (- m 1) n))))
この関数をcurry 化したもの power-c を定義します。
        (define (power-c m) (lambda (n) (if (= m 0) 1 (* n (power (- m 1) n)))))
すると、例えば (power-c 3)は 3 乗を計算する関数を返すようになります。 つまり、あらかじめカリー化しておくことで、 元々の関数の一部が決定した形の関数を返すことが出来たわけです。

>(power 3 2)
8
> (power-c 3)
#<procedure>
> ((power-c 3) 2)
8

少し、先程の func1-with-debug-print を振り返ってみましょう。あの関数も 実は、 (func1-with-debug-print func func-name x) のカリー化された表現だったわけです。そのうえで、例えば、 length に特化した db-length という関数を生成しています。

カリー化などを用いて引数を特定の値に特化したものを作ることが出来れば、 実は最適化を施す余地も生まれます(例: 部分評価)。


99.10.7/ Tomio KAMADA: kamada@cs.kobe-u.ac.jp