OCaml一人勉強会 - レコード、バリアント、ついでに型変数

レコードは名前付きタプルというか、Structというか。そんな感じのものです。よくある例として二次元座標とか。

# type point = { x:int; y:int };;
type point = { x : int; y : int; }
# let pt = {x=3; y=5};;
val pt : point = {x = 3; y = 5}
# pt.x * pt.y;;
- : int = 15

フィールド名を同じにしちゃうようなオイタは型推論の邪魔なのでダメらしいです。

# type hoge = { x:int };;
type hoge = { x : int; }
# {x=3;y=5};;
Characters 0-9:
  {x=3;y=5};;
  ^^^^^^^^^
The record field label y belongs to the type point
but is here mixed with labels of type hoge

用法容量を守って正しくお使い下さい。
バリアントは何かちょっと強い子。色々できる。
例えば色とか。C言語enum的に。

# type color = Black | White | Red | Blue | Green;;
type color = Black | White | Red | Blue | Green
# Black;;
- : color = Black

タグを | で書き連ねる。型名が小文字で始めないといけないのに対し、タグ名は大文字で始めないといけない。
これだけだと大したことないですが、例えば試験の成績を整理する時に0〜100までの点数に加え未受験という情報も持たせたい場合があります。
C言語などでは何らかの整数値(-1とか)を未受験として扱う、とかまあするわけですが、バリアントを使うとそのまま表現できる。

# type score = Score of int | Unexam;;
type score = Score of int | Unexam
# let erai = Score 100;;
val erai : score = Score 100
# let dame = Score 0;;
val dame : score = Score 0
# let hikikomori = Unexam;;
val hikikomori : score = Unexam

しかも再帰的な定義が出来るので二分木とか余裕で書ける。

# type tree = Leaf | Node of (tree * tree);;
type tree = Leaf | Node of (tree * tree)

さて、あとは Leaf タグに適当な型を関連付ければ良いわけですが、わざわざ int_tree とか string_tree とか定義してられません。
ここで C++ の template みたいで、Java の Generic みたいなよくわからない型変数という物を使うことで tree を一般化することができます。

# type 'a tree = Leaf of 'a | Node of ('a tree * 'a tree);;
type 'a tree = Leaf of 'a | Node of ('a tree * 'a tree)

'a は型変数と呼ばれるもので、まあ C++ の <> だと思えばいいと思います(注:ラムダのことではない)。

# let hoge = Leaf "hoge";;
val hoge : string tree = Leaf "hoge"
# let hogehige = Node (Leaf "hoge", Leaf "hige");;
val hoge : string tree = Node (Leaf "hoge", Leaf "hige")

とまあこんな感じでOCamlの型について色々見てきたわけですが、型の表現に関しては割と自由度も高く強そうです。他の関数型言語もそうなのかなあ謎。
こうある程度体系的なプログラミングなんてのはアルゴリズムとデータ構造が中心で、さらに言えばまずはデータ構造ありきだと思っているんですが、その線でいくとOCamlの型システムは非常に良いなあと思うわけです。
一方でこの記述の長さは正直にいっていただけない。

# List.length [1;2;3;4;5];;
- : int = 5
# Array.length [|1;2;3;4;5|];;
- : int = 5

あと普通こういうことはしないんでしょうが、ちょっと勘弁してくれと。

# open List;;
# open Array;;
# length [1;2;3];;
Characters 7-14:
  length [1;2;3];;
         ^^^^^^^
This expression has type 'a list but is here used with type 'b array

Objective ならこういうのできてもいいじゃないかと思うんですができない。

# [1;2;3]#length;;
Characters 0-7:
  [1;2;3]#length;;
  ^^^^^^^
This expression has type int list
It has no method length

メソッド持ってないのかよ…という。まあそんなものもってたら色々差し支えあるのかもしれませんしないのかもしれませんが、ないものは仕方ないです。

OCaml一人勉強会 - クラス

まあクラスも立派な型だし Objective とかいうくらいなので一応触っておきましょうみたいなどうでもいい感じで。

# class mylist init =
        object
                val list_ = init
                method length = List.length list_
        end;;
class mylist : 'a list -> object val list_ : 'a list method length : int end
# let l = new mylist [1;2;3];;
val l : mylist = <obj>
# l#length;;
- : int = 3

まあなんの意味もない感じのクラス mylist を定義しました。わーい。特に書くことはない。


という事で今日はざっと一通り、簡単にですがOCamlの型について色々復習してみました。
非常に長くて、ノイズだらけというか。世のため人のためにならない感じですが一人学習会なので気にしません。
明日は多分パターンマッチングとか末尾再帰とかもうちょっと関数型っぽいことを。

OCaml一人勉強会開始

突然ですがOCamlまた始めます。またって何?という人はこちら。なんと半年以上空いてしまいました。あはは。
思えば色々ありました(以下略)。
まあ何か大分前にやるだけはやったんですが復習ついでに書きます。
追記:OCamlとか関数型言語とか全然知らないので間違ってる所あったら教えてやって下さい。

OCaml一人勉強会 - タプル、リスト、その他コンテナ

Ocaml関数型言語らしいです。関数型言語といえばタプルです(違います)。ということでタプル。

# (1, "hoge", 'a');;
- : int * string * char = (1, "hoge", 'a')
# 1, "hoge", 'a';;
- : int * string * char = (1, "hoge", 'a')

ふむふむ。括弧省略できる。まあゴルフ以外ではする意味ないしゴルフではタプル使わない気がする。
関数型言語といえばリスト略。

# [1; 2; 3;];;
- : int list = [1; 2; 3]

これは明らかに括弧省略無理(違う意味になるのは目に見えている)。
うっかり他の言語などでよく見られる「[a,b,c]」のような形で書いてしまうと…

# [1, 2, 3];;
- : (int * int * int) list = [(1, 2, 3)]

タプルのリストになってしまうのだ!大変だ!
ちゃんと手続き脳の大好きな配列もあるよ。

# [|1; 2; 3|];;
- : int array = [|1; 2; 3|]

リストと配列の違いはまあデータ構造とか勉強してれば分かるよね!まあ他の言語と一緒。

  • リストは append とかが O(1) で強い。at(N) みたいなのは O(N) で弱い。
  • 配列は append とかが O(N) で弱い。at(N) みたいなのは O(1) で強い。

でまあ他にも標準ライブラリにはコンテナとして、Hashtbl とか Set とか Map とか Queue とか Stack とか必要な物は一通りある。Buffer とかいうICFPC2007に向いてそうな文字列のコンテナもある。まあこれらは追々。