但し、Scheme において 関数 と言った場合は、
define で定義されたものだけを指すのではありません。
後述の ラムダ式 等を用いて動的に生成された
関数も含んでいます。これについては、次節で説明します。
では、関数の値というからには、関数を簡単化していくのでしょうか?
でも、関数の等価な変換と言うのは難しいものです。
もともと、関数とは引数とその返り値の写像の事です。
つまり、ある関数と別の関数が等価であるかどうかは写像として同じもので
あるかどうかであるはずです。
例えば、
f(x) = x + x, g(x) = 2 * x の f, g は関数として等価なはずです。
このように、関数の場合、等価な関数の表現は様々です。
関数の等価性を判断するために、写像としての同一性をみようとして
全ての引数とその結果を出して比較しようとしたとしましょう。
しかしながら、引数の範囲が有限であればそれぞれの値を求めて比較することで対応できますが、
無限であるならば機械的に処理することは出来ません。
加えて、関数がある引数に対して停止しない場合はどうなるでしょう。
つまり、ある引数に対して入力を与えては見たけれど、f, g もしばらく返って来ない。
でも、しばらく返って来ないからと言って、未来永劫返って来ないともいえません。
つまり、プログラムの停止性も考える必要があるわけです。
では、いくつかルールを準備して式変換を行う場合を考えましょう。
たとえば、前述のように f と g は等価であるという
rule を準備することは可能でしょう。しかしながら事はそう単純ではありません。
実は、このように一般の関数(すなわちアルゴリズム)について、
その等価性を求めるアルゴリズムは存在しません(決定不能:undecidable である)。
というわけで、
関数の等価性を返す関数もありません。
Scheme の eq?, eqv, equal を関数に対して適用した場合、
ある同じ場所で定義されたものについて #t を返すことはあっても、
等価な関数全てに対して #t を返してくれるわけではありません。
eval は、(eval e)と書くことで、S式として与えられた
e を現在の環境で評価するというものです。
#include
このように、C では静的に定義された関数(のポインタ)を値の様に扱っています。
apply_2_3 は高階関数といっても良いかも知れません。
関数の返り値に関数(のポインタ)を返すこともできるでしょう。
h(x) = (if (< x 0) (- x (abs x)) (* x 2))
この h も f と等価なはずです。
この例は、 x < 0 においては、 (abs x) = -x であることを利用しています。
つまり、式変換においても環境の解析も必要ですし、
色々な環境を考慮したルールをつくる必要があります。
加えて、再帰がある場合を考えなくては行けません。
つまり、ここで無限の環境を相手にしなくてはいけなくなるかもしれないのです。
> '(+ 1 2)
(+ 1 2)
> (eval '(+ 1 2))
3
これは、評価の結果得られたS式をもう一度 interpreter にかけなおしているとも
いえます。
これを使うと、与えられた式を S 式として好きな形に編集をし、
最後に eval に書けて実行するなどという芸当も出来てしまいます。
ですから、自作の apply なども簡単に書けてしまいます。
> (define (my-apply func args)
(eval (cons func args)))
> (my-apply + '(1 2 3))
6
#'func-name
と書いて下さい。
また、変数名 var を関数に束縛している場合でも、
(var args...)
という形で適用はできません。
(funcall var args...)
とするか、
(apply var (args...))
とする必要があります。
99.10.7/ Tomio KAMADA: kamada@cs.kobe-u.ac.jp