補足 (高階関数)


C 言語で関数を値として用いる
Scheme では 関数を first class value として用いることができるといいました。 実は、C などでも似たような事はする事は可能です。関数への pointer を用います。 例えばこんな具合。
#include 
#include 

typedef int (*func_t)(int, int) ;

int add (int x, int y) { 
    return (x + y);
}

int foo (int x, int y) {
    return ((x + 1) * y);
}

int apply_2_3 (func_t func) {
    return (*func)(2, 3);
}

int main () {
    func_t func0 = add;
    func_t func1 = foo;

    printf("(%d %d)\n", apply_2_3(func0), apply_2_3(func1));
    return 0;
}
このように、C では静的に定義された関数(のポインタ)を値の様に扱っています。 apply_2_3 は高階関数といっても良いかも知れません。 関数の返り値に関数(のポインタ)を返すこともできるでしょう。

但し、Scheme において 関数 と言った場合は、 define で定義されたものだけを指すのではありません。 後述の ラムダ式 等を用いて動的に生成された 関数も含んでいます。これについては、次節で説明します。

関数の値と等価性の続き
そもそもこれまでは式の値と言うのは、 式を簡単化していってこれ以上簡単にならないという式のことだと言って来ました。 これは、普通の式については、簡単化していくとある形に簡単化(簡約)されていき、 その簡単化の過程が関数型プログラミングにおける計算でした。

では、関数の値というからには、関数を簡単化していくのでしょうか? でも、関数の等価な変換と言うのは難しいものです。 もともと、関数とは引数とその返り値の写像の事です。 つまり、ある関数と別の関数が等価であるかどうかは写像として同じもので あるかどうかであるはずです。 例えば、 f(x) = x + x, g(x) = 2 * x f, g は関数として等価なはずです。 このように、関数の場合、等価な関数の表現は様々です。

関数の等価性を判断するために、写像としての同一性をみようとして 全ての引数とその結果を出して比較しようとしたとしましょう。 しかしながら、引数の範囲が有限であればそれぞれの値を求めて比較することで対応できますが、 無限であるならば機械的に処理することは出来ません。 加えて、関数がある引数に対して停止しない場合はどうなるでしょう。 つまり、ある引数に対して入力を与えては見たけれど、f, g もしばらく返って来ない。 でも、しばらく返って来ないからと言って、未来永劫返って来ないともいえません。 つまり、プログラムの停止性も考える必要があるわけです。

では、いくつかルールを準備して式変換を行う場合を考えましょう。 たとえば、前述のように fg は等価であるという rule を準備することは可能でしょう。しかしながら事はそう単純ではありません。

    h(x) = (if (< x 0) (- x (abs x)) (* x 2))   
この hf と等価なはずです。 この例は、 x < 0 においては、 (abs x) = -x であることを利用しています。 つまり、式変換においても環境の解析も必要ですし、 色々な環境を考慮したルールをつくる必要があります。 加えて、再帰がある場合を考えなくては行けません。 つまり、ここで無限の環境を相手にしなくてはいけなくなるかもしれないのです。

実は、このように一般の関数(すなわちアルゴリズム)について、 その等価性を求めるアルゴリズムは存在しません(決定不能:undecidable である)。 というわけで、 関数の等価性を返す関数もありません。 Scheme の eq?, eqv, equal を関数に対して適用した場合、 ある同じ場所で定義されたものについて #t を返すことはあっても、 等価な関数全てに対して #t を返してくれるわけではありません。

map, apply 関連情報
Common Lisp や、 Emacs Lisp でおなじことをしたい人に
まず、これらの処理系では、関数名とその実体が異なります。 関数を値として使いたい場合は、
         #'func-name 
と書いて下さい。 また、変数名 var を関数に束縛している場合でも、
        (var args...) 
という形で適用はできません。
        (funcall var args...) 
とするか、
        (apply var (args...)) 
とする必要があります。


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