OCaml一人勉強会 - 参照型
さて前回、アキュムレータジェネレータが書けないと書いた理由は、変数の破壊的代入が必要になるためです。ですよね?
OCamlは関数型言語ですが、純粋な関数型言語ではありません。純粋な関数型言語としては最近ではHaskell等が有名です。
そもそも純粋って何よ、という話ですが、純粋な関数型言語では副作用は存在しません。これによって参照透明性が保たれます。
はい意味不明ですね。もう少し分かりやすく説明すると、純粋な関数型言語では変数に対する代入はできないということです。
それって意味あるの?はいあります。定義された時点で変数の値が決定するので分かりやすくなります。なるんです!!この性質を参照透明性と言ったりします。
話を戻します。OCamlは純粋な関数型言語ではないので、変数の破壊的代入ができます…が普通は出来ません。変数の破壊的代入が必要な時は、参照型という物を使います。
# let x = ref 0;; val x : int ref = {contents = 0}
何かこれ見たことありますね。はいレコードです。実際のところ参照型 ref は下のように定義されてるようです。
# type 'a ref = { mutable contents : 'a };; type 'a ref = { mutable contents : 'a; }
以前のレコードの回には触れませんでしたが、実はレコードのフィールドは代入することができます。
mutable という初めてみる予約語が出てきましたが、これはレコードのそのフィールドが書き換えられることを示します。
さて x に適当な値を代入して変数を参照してみましょう。代入は <- で行います。
# x.contents <- 4;; - : unit = () # x.contents;; - : int = 4
これだと長くて面倒、ということで参照型では特別に下のような操作が可能です。
# x := 4;; - : unit = () # !x;; - : int = 4
恐らくこんな感じの定義がされているはずです。
# let (!) x = x.contents;; val ( ! ) : 'a ref -> 'a = <fun> # let (:=) x y = x.contents <- y;; val ( := ) : 'a ref -> 'a -> unit = <fun>
さて参照型の使い方が分かった所でアキュムレータジェネレータを書いてみましょう。
# let f x = let n = ref x in fun y -> n := !n + y; !n;; val f : int -> int -> int = <fun> # let g = f 10;; val g : int -> int = <fun> # g 1;; - : int = 11 # g 2;; - : int = 13 # g 3;; - : int = 16
めでたしめでたし。
ちなみにアキュムレータジェネレータとは、
数nを取り、「数iを取ってnをiだけ増加させ、その増加した値を返す関数」を返すような関数
のことらしいです。最初に言え。