たとえば、fun x -> e という式は関数を表すが、 いったいこれの意味は何かまだよくわかっていないうちに、その形式を処理するプログラムを書き始めるのである。
このようにする理由は、時間を節約するためではなく、 「処理系を作成することが、そのプログラム言語を本当に理解するための近道である」という考え方による。
書きたいプログラムの種類(アプリケーション分野)によっては、 実行効率などのために、これらのimpureな機能がどうしても必要な場面もあるのだが、 本実験では、関数型プログラム言語の表現力を理解してもらうため、 処理系の中核部分では、impure な機能を使わないという方針で行く。 impureな機能を使うのは、 処理系と実世界のインタフェース部分(ファイルへの読み書き、 端末へのプリント)や、エラーが起きたときの例外処理などに限定する。
この方針であっても、 プログラム言語の機能を理解するためには十分な処理系が書ける、 ということを本実験で体験してほしい。
以上の方針のもとに、本実験で必要な機能を厳選したものが、 以下の言語プリミティブである。
ヒント: x,y がともに正の整数であるとき、xとyの最大公約数をGCD(x,y)とす ると、以下が成立する。(Euclidの互除法)
GCD(x,y) = x if x = y GCD(x,y) = GCD(x-y, y) if x > y GCD(x,y) = GCD(x, y-x) if x < y
テスト・データ: OCamlでは「2引数関数」は、通常 gcd m n という形で記述 するので、ここでもそういう形に対するテストとする。)
gcd 30 50 --> 10 gcd 30 51 --> 3 gcd 30 52 --> 2 gcd 30 0 --> 30 gcd 0 30 --> 30 gcd (-3) 6 --> 3 (* 負の数には、かっこをつけること *) gcd (-3) (-6) --> 3 (* 「最大」公約数なので -3 でなく 3 になる *) gcd 0 0 --> ? (* GCD(0,0)は未定義なので何を返してもよい。停止しなくてもよい *)
FIB(1) = FIB(2) = 1 FIB(n+2) = FIB(n) + FIB(n+1) if n > 0となる FIB を計算する関数fib を定義してほしい。
(発展課題) 上記の定義を単純に実装すると非常に効率が悪い。 そこで、効率を改善したプログラムを実装せよ。 (ヒント: OCaml では、2つの数mとn の組は (m,n) という式で表現できる。)
テスト・データ:
fib 3 --> 2 fib 4 --> 3 fib 5 --> 5 fib 7 --> 13 fib 10 --> 55 fib 20 --> 6765 fib 30 --> 832040 fib (-1) --> ? (* FIB(-1)は未定義なので何を返してもよい。停止しなくてもよい *)
テスト・データ:
prime 1 --> 2 prime 2 --> 3 prime 3 --> 5 prime 4 --> 7 prime 5 --> 11 prime (-1) --> (* 未定義なので何を返してもよい。停止しなくてもよい *)
substring "abcabc" "abc" = 0 substring "abcabc" "bca" = 1 substring "abcabc" "bcd" = -1である。
亀山 幸義