OCaml一人勉強会 - 演算子の使い方と定義
触れたくなくて今まで華麗にスルーしてきましたが、手続き型言語の多くとは違い、OCamlの演算子は非常に融通が利きません。
# let x = 1 * 2.2;; Characters 12-15: let x = 1 * 2.2;; ^^^ This expression has type float but is here used with type int
まあ勿論これは型推論のためなんですが…こういうときのために float_of_int, int_of_float という関数が用意されています。
# float_of_int(1);; - : float = 1. # int_of_float(1.1);; - : int = 1
気を取り直して。
# let x = float_of_int(1) * 2.2;; Characters 8-23: let x = float_of_int(1) * 2.2;; ^^^^^^^^^^^^^^^ This expression has type float but is here used with type int
エーッ。
これは演算子 * が int 型の引数を受け取る演算子だからです。
float 型の引数を受け取る演算子は *. になります。
# let x = float_of_int(1) *. 2.2;; val x : float = 2.2
まあ勿論これも型推論のためなんですが…長い道のりでした。
若干話は変わりますが、単項演算子 - も中々癖物です。こんなコードを書くと…
# let succ x = x + 1;; val succ : int -> int = <fun> # succ -3;; Characters 0-4: succ -3;; ^^^^ This expression has type int -> int but is here used with type int
(succ) (-) (3) と解釈されます。ちゃんと括弧を使いましょう。
# succ (-3);; - : int = -2
ちなみにこの - ですが、 int にも float にも使えてしまいます。勿論 -. もあります。
# -1;; - : int = -1 # -1.2;; - : float = -1.2 # -.1.2;; - : float = -1.2
勿論そんな特例があるわけではなく、リテラルとして使えるという話のようです。
float-literal ::= [-] { 0...9 }+ [. { 0...9 }] [(e| E) [+| -] { 0...9 }+]
なのでこういうのはダメ。
# let f = 1.0;; val f : float = 1. # -f;; Characters 1-2: -f;; ^ This expression has type float but is here used with type int # -.f;; - : float = -1.
なんだかなー。
さて OCaml では括弧で括ることで演算子を定義することが出来ます。ちょっとイタズラして、整数の加算を減算にしてしまいましょう。
# let (+) x y = x - y;; val ( + ) : int -> int -> int = <fun> # 3 + 2;; - : int = 1
こんなことする悪い子には罰があたります。具体的にどういう罰かというと、中々直せなくなります。
# let (+) x y = x + y;; val ( + ) : int -> int -> int = <fun> # 3 + 4;; - : int = -1
アッー。
まあこんな感じで直しときましょう。
# let (+) x y = x + -y;; val ( + ) : int -> int -> int = <fun>
寄り道しすぎた。
えーと OCaml では割と自由に演算子の定義が出来ます。和と差のリスト、積 と 商を返す演算子は下のように。
# let (+-) x y = [x + y; x - y];; val ( +- ) : int -> int -> int list = <fun> # let ( */) x y = [x * y; x / y];; val ( */ ) : int -> int -> int list = <fun>
(*/) と書くとコメントと解釈されるので注意。勘弁してくれという感じですが。
優先順位は一文字目の記号で決まります。
# let (+?) x y = x - y;; val ( +? ) : int -> int -> int = <fun> # let ( *?) x y = x / y;; val ( *? ) : int -> int -> int = <fun> # 4 +? 4 *? 2;; - : int = 2
頭に ~ をつけることで単項演算子も定義できます。 - 以外定義できないみたいですが。
まあそんな感じ。