読者です 読者をやめる 読者になる 読者になる

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とか。古いのかも。