2008-01-01から1ヶ月間の記事一覧

多相性と書き換え可能データ

let で名前の付けられる式が値でない場合,多相性に制約がつくことがある。値でないとはたとえば参照などだ。 # let x = ref [];; val x : '_a list ref = {contents = []}x は参照で,中身は空のリストだ。空だから何のリストでもいい(多相)はずで,型も …

配列

配列はリストと似ているけど 長さが生成時に固定される 各要素に直接アクセスできる 書き換え可能 という点で違う。各要素は同じ型でないといけないのはリストと同じ。生成: # let ary = [| 1; 2; 3; 4; 5 |];; val ary : int array = [|1; 2; 3; 4; 5|] # …

書き換え可能なデータ構造:文字列

実は,文字列は書き換えが可能。たとえば次のような文字列があったとして: # let s = "life";; val s : string = "life" # s;; - : string = "life"次のように書き換えができる。 # s.[2] <- 'k';; - : unit = () # s;; - : string = "like"新しい値ができる…

構造的等価性と物理的等価性

2つのデータを比べたとき,「値として等しいこと」を構造的等価性(structural equality)という。「値として」だけでなく,メモリ上の同じ位置を占めていることを物理的等価性(physical equality)という。 OCaml には構造的等価性を調べる演算子 = と物…

書き換え可能なレコード

レコードを宣言するときにフィールド名の前に mutable キーワードをつけることで,書き換え可能にすることができる。 # type teacher = {name : string; mutable office : string};; type teacher = { name : string; mutable office : string; }これで offi…

参照

書き換え可能なレコードの特殊な場合で,フィールド1つだけを持つ場合を伝統的に参照(reference)という。参照には特別な書き方がある。 まず,参照の生成には ref 関数。 # let p = ref 4;; val p : int ref = {contents = 4}ref は初期値を引数にとって…

書き換え可能なデータ構造

OCaml には書き換え可能なデータ構造がある。 書き換え可能レコード(mutable record) 参照(reference) 配列(array)

exn型,exception宣言

Not_found とか Division_by_zero とかいう例外は,じつは exn型のコンストラクタ。例外コンストラクタと呼ぶ。 コンストラクタの型を見ると # Division_by_zero;; - : exn = Division_by_zeroexn型であることがわかる。同様に raise の型も。 # raise;; - :…

練習問題 7.2

整数リストの要素すべての積を返す関数 prod_list を定義しなさい。リスト要素の一つでも 0 が含まれている場合には,prod_list の適用結果は常に 0 になるので,例外処理を使用 して,0 を発見したら残りの計算を行わずに中断して 0 を返すように定義しなさ…

例外を発生させる raise

OCaml には例外処理の仕組みがある。例外を発生させるには raise を使う。raise は例外の名前を引数にとる。 fact関数の引数が負の時に例外を発生する例: # let rec fact n = if n < 0 then raise (Invalid_argument "fact: negative argument") else if n =…

例外を処理する try

発生した例外を捕捉して処理するのが try。 # try fact (-2) with Invalid_argument _ -> 0;; - : int = 0 # try fact 3 with Invalid_argument _ -> 0;; - : int = 6match によるパターンマッチングに似ている。まず try と with に囲まれた部分を評価し,…

ヴァリアントの応用:多相的ヴァリアント

多相的関数が型情報をパラメータ化できるのと同じように,ヴァリアントの定義の一部をパラメータ化することができる。 # type 'a mylist = Nil | Cons of 'a * 'a mylist;; type 'a mylist = Nil | Cons of 'a * 'a mylist'a がパラメータ化された部分。この…

多相的レコード

多相的な定義はレコードでもできる。次の定義は,既存のデータに「位置情報」を付け加える,というもの。 # type 'a with_location = {loc_x : float; loc_y : float; body : 'a};; type 'a with_location = { loc_x : float; loc_y : float; body : 'a; }文…

ヴァリアントの応用:列挙型

引数をとらないコンストラクタのみでヴァリアントを作れば,いわゆる列挙型になる。 # type color = Black | Blue | Red | Magenta | Green | Cyan | Yellow | White ;; type color = Black | Blue | Red | Magenta | Green | Cyan | Yellow | White

ヴァリアントの応用:再帰的ヴァリアント

type宣言において,コンストラクタの引数に今宣言しようとしているヴァリアントを使うことができる。つまり再帰的な宣言。 以下は0を含む自然数(というか正の整数)を表す型 nat を宣言する例。 ゼロは自然数である 自然数より1大きい数は自然数である これ…

ヴァリアント

type 宣言を使って宣言できるデータにはヴァリアントというのもある。おおざっぱに言うと「作り方に何種類か方法があるようなデータ」。Haskellでいう代数的データ型と同じと思っていいのかな。 たとえば次のようなものがヴァリアント。 # type figure = Poi…

ヴァリアントのコンストラクタ

コンストラクタの名前は: 一文字目が大文字 二文字目以降が英数字(A..Z, a..z, 0..9),アンダースコア(_)またはプライム(') であるような任意の長さの文字列。

レコード

レコードとはいくつかの値に名前を付けてまとめて扱えるようにしたデータ。構造体のようなもの。名前と値をあわせてフィールド,名前をフィールド名と呼ぶ。 新しいレコードの型を宣言するには type 宣言を使う。 # type student = {name : string; id : int…

レコードのフィールド名は重ならないように

レコードの型を宣言するときの注意。既存の型と同じフィールド名を使ってしまうと,先に宣言した型のフィールド名が使えなくなってしまう。たとえば前エントリの student とその値が存在している状態で: # type student = {name : string; id : int};; type…

練習問題 5.2 (つづき)

5. 2つの[a1; ...; an] と [a1; ...; bn] を引数として,[(a1,b1); ...; (an,bn)] を返す関数 zip (与えられたリストの長さが異なる場合は長いリストの余った部分を捨ててよい)。 # let rec zip l1 l2 = match (l1, l2) with ([], _) -> [] | (_, []) -> […

練習問題 5.2

次の関数を定義しなさい。 1. 正の整数 n から 1 までの整数の降順リストを生成する関数 downto1。 # let rec downto1 n = if n = 0 then [] else n :: downto1 (n-1);; val downto1 : int -> int list = <fun> # downto1 6;; - : int list = [6; 5; 4; 3; 2; 1] </fun>…

クイックソート

Haskell で一番有名(?)なコードを OCaml で書いてみた。 # let rec qsort = function [] -> [] | hd::tl -> qsort (List.filter (fun x -> x < hd) tl) @ [hd] @ qsort (List.filter (fun y -> hd <= y) tl) ;; val qsort : 'a list -> 'a list = <fun> # qsor</fun>…

ガード付きマッチング節

パターンに条件を付加するのがガード。 # let abs' = function 0 -> 0 | n when n > 0 -> n | n -> (-1) * n ;; val abs' : int -> int = <fun>2番目の when n > 0 がガード。パターンは3番目と同じだけどここで条件分けをしている。3番目のパターンは条件が付い</fun>…

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;</fun>…

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;</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 li…

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";;…

リスト

あいだが開いてしまった。多相や型推論の部分はどうにも頭の整理できないので,先に進もう。リストは全体を [ と ] で囲って,要素を ; で区切る。[]は空リストを表す。 # [1;2;3];; - : int list = [1; 2; 3] # [];; - : 'a list = [] ||< ::(コンスオペレ…

match式とリストパターン

引数のパターンマッチには match式をつかう。リストパターンは :: を使える。たとえば: # let rec sum l = match l with [] -> 0 | hd::tl -> hd + sum tl ;; val sum : int list -> int = <fun> # sum [1;3;5;7;9];; - : int = 25</fun>

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> # l</fun>…