Help us understand the problem. What is going on with this article?

OCaml 入門その1 OCamlの基本と型推論

More than 1 year has passed since last update.

関数型言語

関数型言語 => プログラムの実行 = 式の実行
式の実行 => 式を評価(eval)して値(value)を得ること

ocaml_eval_expression.png

基本データ型

整数型 (int)

プリフィックス
0b で2進数
0o で8進数
0x で16進数

- : int = 351
# 12345;;
- : int = 12345
# 0o523;;
- : int = 339
# 0xffff;;
- : int = 65535

実数型 (float)

少数も扱える。
e表記も可能。

# 3.141;;
- : float = 3.1415
# 1.04e10;;
- : float = 10400000000.
# 25e-15;;
- : float = 2.5e-14

文字型 (char)

半角英数字(ascii文字) で1重引用符で囲む

# 'a';;
- : char = 'a'
# '\101';;
- : char = 'e'
# '\n';;
- : char = '\n'

文字列型 (string)

二重引用符で囲む。
文字列.[添字] で1文字を取り出せる。

# "string";;
- : string = "string"
# "string".[0];;
- : char = 's'

型変換

X型からY型へ変換する関数は Y_of_X という命名規則がある。

# float_of_int 5;;
- : float = 5.
# int_of_float 5.;;
- : int = 5
- # string_of_int 123;;
- : string = "123"
# int_of_string "123";;
- : int = 123

(型名 * 型名...)

# (1, 2);;
- : int * int = (1, 2)
# ('a', 1, "str", 4.3);;
- : char * int * string * float = ('a', 1, "str", 4.3)
# ((1, 2), ('a', "str"));;
- : (int * int) * (char * string) = ((1, 2), ('a', "str"))

変数定義(束縛)

let定義 (let defenition)

let 変数名 = 式

# let hoge = 1;;
val hoge : int = 1

同時定義可能

let 変数名 = 式1 and 式2

# let a = 1 and b = 2;;
val a : int = 1
val b : int = 2

関数定義

let 関数名 パラメータ = 式

# let twice s = s ^ s;; 
val twice : string -> string = <fun>

パラメータを囲む () は省略可能。

let式 (let expression)

let定義 とは違う。
関数内の一時的に使う変数(局所変数)を定義するための式。

let 変数 = 式1 in 式2

# let four_times s =
  let twice = s ^ s in
  twice ^ twice;;
val four_times : string -> string = <fun>

再帰定義

let rec で定義する。
rec を使うと関数定義内で、定義中の関数の名前を参照できるようになる。

階乗の例

# let rec fact x =
    if x <= 1 then 1 else x * fact (x - 1);;
val fact : int -> int = <fun>
# fact 5;;
- : int = 120

相互再帰

二つ以上の関数がお互いを呼び合うスタイルの再帰定義。

let rec 関数名1 パラメータ1 = 式1 and 関数名2 パラメータ2 = 式2 and ...

# let rec even n =
    match n with
    | 0 -> true
    | x -> odd (x-1)
  and odd n =
    match n with
    | 0 -> false
    | x -> even (x-1);;
val even : int -> bool = <fun>
val odd : int -> bool = <fun>

# even 10;;
- : bool = true
# even 3;;
- : bool = false
# odd 3;;
- : bool = true
# odd 10;;
- : bool = false

無名関数

fun パラメータ = 式

let f パラメータ = 式let f = fun パラメータ = 式 の糖衣構文。
fun は可能な限り先まで関数定義だと認識するため、必要なところまで () で区切ると良い。

高階関数

関数を引数に取る関数の定義

# let twice f x = f (f x);;
val twice : ('a -> 'a) -> 'a -> 'a = <fun>
# twice (fun x -> x * x) 3;;
- : int = 81

# let fourth x = 
    let square y = y * y in
    twice square x;;
val fourth : int -> int = <fun>
# fourth 3;;
- : int = 81

関数のカリー化

# let concat_curry s1 = fun s2 -> s1 ^ s2 ^ s1;;
val concat_curry : string -> string -> string = <fun>
# concat_curry "a";;  (* 部分適用 *)
- : string -> string = <fun>
# (concat_curry "a") "b";;
- : string = "aba"

カリー化の糖衣構文

以下の定義は

let concat_curry s1 s2 = s1 ^ s2 ^ s1;;

以下と同義

let concat_curry s1 = fun s2 -> s1 ^ s2 ^ s1;;

つまり、パラメータを並べると

# let fuga x y z = x + y + z;;
val fuga : int -> int -> int -> int = <fun>

実際には以下のように展開されている。

# let hoge x = fun y -> fun z -> x + y + z;;
val hoge : int -> int -> int -> int = <fun>

関数適用は左結合 なので、以下のように展開される。

f x y z => (((f x) y) z)

関数型構築子は右結合 なので、以下のように解釈する。

int -> int -> int -> int = <fun>

int -> (int -> (int -> int)) = <fun>

演算子の定義

中置演算子はカリー化された関数として定義されている。

# (+);;
- : int -> int -> int = <fun>
# (+) 1 2;;
- : int = 3

自分で演算子を定義する場合は

  1. 前置演算子
    • 1引数関数として定義する
  2. 中置演算子
    • 2引数(カリー化)関数として定義する

演算子の定義に使える文字種は制限されているので注意する。

型推論

比較演算子(=など)は整数型・実数型など複数の型で使える => 多相性(ポリモーフィズム)を持つ。
多相性がある型システムを 多相的型システム と呼ぶ。

型スキーム・パラメトリック多相

型の出現パターンを示す、変数のようなもの。
'a'b のように表現される。

# let twice f x = f (f x);;
val twice : ('a -> 'a) -> 'a -> 'a = <fun>
# let first (x, y) = x;;
val first : 'a * 'b -> 'a = <fun>

実行の際に、OCaml の型システムが型スキーム('aなど)を具体化(具体的な型に当てはめる)する。
型情報をパラメータ化することによる多相性を パラメトリック多相 と呼ぶ。

型変数の一般化

型変数('_aなど)を一般化('aのような型スキーム)するための条件の 1つ は以下。

定義の右辺の式がそのまま値であれば型変数を一般化してよい

値として扱われるもの => 計算を必要としない式
例として

  1. 関数の宣言
  2. 定数

値に評価されるために、計算が必要となる式は値として扱われない。

(* 恒等関数を定義する *)
# let id x = x;;
val id : 'a -> 'a = <fun>

(* '_a は型スキームではない 
   id id は値になるためには何か実引数が必要で、計算が必要。
*)
# let id' = id id;; 
val id' : '_a -> '_a = <fun>

(* 
   こっちは型スキーム
   パラメータ x を追加することで id' は関数定義となる。
*)
# let id' x = id id x;;
val id' : 'a -> 'a = <fun>

参考文献

プログラミング in OCaml

非常に分かりやすく、OCamlを学ぶ際にオススメです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした