ガード付きマッチング節

パターンに条件を付加するのがガード。

# let abs' = function
0 -> 0
| n when n > 0 -> n
| n -> (-1) * n
;;
val abs' : int -> int = <fun>

2番目の when n > 0 がガード。パターンは3番目と同じだけどここで条件分けをしている。3番目のパターンは条件が付いていないので残りの場合(n < 0 の場合)にマッチする。

# abs' (-1);;
- : int = 1
# abs' 8;;
- : int = 8
# abs 0;;
- : int = 0

3番目のパターンにもガードをつけると警告がでる。

# let abs'' = function
0 -> 0
| n when n > 0 -> n
| n when n < 0 -> (-1) * n
;;
Characters 12-82:
Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
1
(However, some guarded clause may match this value.)
............function
0 -> 0
| n when n > 0 -> n
| n when n < 0 -> (-1) * n
val abs'' : int -> int = <fun>

パターンマッチングが網羅的でないといっている。要するにどのパターンにもマッチしない可能性があるというわけだ。

これは, when の条件は false に可能性がある,という見積もりをするせい。そのため,上に書いたような警告がでる。最初のコードでは3番目のパターンには条件が付いていないから,n がどんな値でもマッチすることになり,警告もでない。

2つで引数にパターンマッチするには

タプルにしてしまえばいい。ただし,function構文はつかえない。

# let rec zip l r =
match (l, r) with
([], _) -> []
| (_, []) -> []
| (h1::t1, h2::t2) -> (h1, h2) :: zip t1 t2
;;
val zip : 'a list -> 'b list -> ('a * 'b) list = <fun>
# zip [1;2;3;4] ['a';'b';'c'];;
- : (int * char) list = [(1, 'a'); (2, 'b'); (3, 'c')]

パターンの中の _ はワイルドカード。

List.fold_left と List.fold_right

List.fold_left は左から,List.fold_right は右からたたみ込む。

# List.fold_left (fun x y -> "(" ^ x ^ "," ^ y ^ ")") "A" ["B";"C";"D"];;
- : string = "(((A,B),C),D)"
# List.fold_right (fun x y -> "(" ^ x ^ "," ^ y ^ ")") ["A";"B";"C"] "D";;
- : string = "(A,(B,(C,D)))"

引数の順番が違うので,ちょっと注意が必要。

# List.fold_left;;
- : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = <fun>
# List.fold_right;;
- : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = <fun>

List.map

List.map はリストの各要素に関数を適用する。

# List.map (fun x -> x * x) [1;2;3;4;5];;
- : int list = [1; 4; 9; 16; 25]

実装してみる。

# let rec map' f l =
match l with
[] -> []
| hd::tl -> f hd :: map' f tl
;;
val map' : ('a -> 'b) -> 'a list -> 'b list = <fun>
# map' (fun x -> x * 10) [1;2;3;4;5];;
- : int list = [10; 20; 30; 40; 50]

function構文を使って,

# let rec map'' f = function
[] -> []
| hd::tl -> f hd :: map'' f tl
;;
val map'' : ('a -> 'b) -> 'a list -> 'b list = <fun>
# map'' (fun x -> x * 10) [1;2;3;4;5];;
- : int list = [10; 20; 30; 40; 50]

function構文

match式によるパターンマッチを function構文で書くことができる。パターンマッチのための仮引数が現れないのがミソ。

# let rec sum_list = function
[] -> 0
| hd::tl -> hd + sum_list tl
;;
val sum_list : int list -> int = <fun>
# sum_list [1;2;3;4;5;6;7;8;9;10];;
- : int = 55

reverse

reverse はリストを逆順にする。

# let rec reverse l =
match l with
[] -> []
| hd::tl -> (reverse tl) @ [hd]
;;
val reverse : 'a list -> 'a list = <fun>
# reverse [1;2;3;4;5];;
- : int list = [5; 4; 3; 2; 1]

List.append

List.append は2つのリストを連結する。

# List.append [1;2;3] [4;5;6];;
- : int list = [1; 2; 3; 4; 5; 6]

実装してみる。

# let rec append' l1 l2 =
match l1 with
[] -> l2
| hd::tl -> hd :: append' tl l2
;;
val append' : 'a list -> 'a list -> 'a list = <fun>
# append' [1;2;3] [4;5;6];;
- : int list = [1; 2; 3; 4; 5; 6]

リストの連結は中置演算子 @ でもできる。

# ['a';'b';'c'] @ ['d';'e';'f'];;
- : char list = ['a'; 'b'; 'c'; 'd'; 'e'; 'f']

List.length

List.length はリストの長さを返す。

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

実装してみる。

# List.length [1;2;3;4;5];;
- : int = 5
# let rec length' l =
match l with
[] -> 0
| hd :: tl -> 1 + length' tl
;;
val length' : 'a list -> int = <fun>
# length' [1;2;3;4;5];;
- : int = 5

リスト

あいだが開いてしまった。多相や型推論の部分はどうにも頭の整理できないので,先に進もう。

リストは全体を [ と ] で囲って,要素を ; で区切る。[]は空リストを表す。

# [1;2;3];;
- : int list = [1; 2; 3]
# [];;
- : 'a list = []
||<
::(コンスオペレーター)で要素をリストの先頭に追加する。
>||
# 1 :: [2;3;4;5];;
- : int list = [1; 2; 3; 4; 5]