GCamlで複素数
GCamlというのは簡単に説明すると、OCamlにOverloadingを加えた言語。ただし、Type Classではありません。
さあ、行ってみよう。
$ cat complexsample.ml let print_complex ppf c = Format.fprintf ppf "%+f%+fi" c.Complex.re c.Complex.im let (+) = generic Complex.add | (+) let (-) = generic Complex.sub | (-) let ( * ) = generic Complex.mul | ( * ) let (/) = generic Complex.div | (/) let ( ** ) = generic Complex.pow | ( ** ) let log = generic Complex.log | log let exp = generic Complex.exp | exp let e = generic { Complex.re = 2.718281828; Complex.im = 0.0 } | 2.718281828 let pi = generic { Complex.re = 3.141592; Complex.im = 0.0 } | 3.141592
ここでgenericがオーバーローディングがある値を定義するための構文(Genericsとは関係がありません。多分)。+、-などを適当にoverloadingしておきます。Complex.t -> int -> Complex.tとかも考えられますが、めんどくさいので省略。
G'Caml version 3.09+dev20 (2005-04-04) # #use "complexsample.ml";; val print_complex : Format.formatter -> Complex.t -> unit = <fun> val ( + ) : [| Complex.t -> Complex.t -> Complex.t | int -> int -> int |] = <generic> val ( - ) : [| Complex.t -> Complex.t -> Complex.t | int -> int -> int |] = <generic> val ( * ) : [| Complex.t -> Complex.t -> Complex.t | int -> int -> int |] = <generic> val ( / ) : [| Complex.t -> Complex.t -> Complex.t | int -> int -> int |] = <generic> val ( ** ) : [| Complex.t -> Complex.t -> Complex.t | float -> float -> float |] = <generic> val log : [| Complex.t -> Complex.t | float -> float |] = <generic> val exp : [| Complex.t -> Complex.t | float -> float |] = <generic> val e : [| Complex.t | float |] = <generic> val pi : [| Complex.t | float |] = <generic> # #install_printer print_complex;;
ちなみに、1年弱更新してないので古いかもしれません。ここでeやpiなどがどうなるかというと…
# e;; - : { 'a < [| Complex.t | float |] } => 'a = <generic> # (e:float);; - : float = 2.718281828 # (e:Complex.t);; - : Complex.t = +2.718282+0.000000i
となります。
# e ** (pi * Complex.i);; - : Complex.t = -1.000000+0.000001i # exp (pi * Complex.i);; - : Complex.t = -1.000000+0.000001i # log (-1.0);; - : float = nan # log {Complex.re = -1.0; Complex.im = 0.0};; - : Complex.t = +0.000000+3.141593i
eとかpiとかの型はComplex.iがあることから推論してくれます。そこそこ便利なのでは。(型がわからなかったら、どこまでも型抽象を引きずっていくはず)
OCamlと比べると、(Type Classが無くても)オーバーローディングのおかげでずいぶんとすっきりしたと思います。(ちなみに(再帰とか)ちょっと複雑になるとすぐ型が読めなくなります)
ところで、OCamlとちょっと計算結果が違うのが気になったりならなかったり。log(-1.0)のnanとか。古いのかも。