7. Objective Camlの親戚一覧 ML系列 (Objective Camlと同類) F# (for .Net) http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/default.aspx Standard ML of New Jersey (SML/nj) http://www.smlnj.org/ Alice http://www.ps.uni-saarland.de/alice/ SML# http://www.pllab.riec.tohoku.ac.jp/smlsharp/ja/ Haskell Closure Concurrent Clean Lisp/scheme
20. インストール Mac: Intel版を公式サイトからダウンロード MacPortsからインストール Unix/Linux: ソースから頑張る gccインストールよりは確実に楽です パッケージはいろいろあります Gentoo/Redhat/Debian for Linux Ports/pkgsrc for *BSD
40. 関数/条件分岐 変数の定義・関数の定義 letfoobar = 10;; letfoobar = 15 andfoobar2 = 20;; lettestfunc a = print_string “answer is “; print_int (a + 30) ;; lettestfunc a b = a + b;; lettestfun a:int = a;; スコープ付き変数・関数の定義 letfoobar = 10 in foobar + 10 ;; 名前無し関数(closure)定義 fun a -> a + 10;; let testfunc a b = a + b;; ===== let testfunc = fun a b -> a + b;; これは再代入とは違うので注意!! (新規に領域を作って名前をbindしている) 連続した命令は”;”で分ける 引数の型は明示的にも宣言可
41. 関数/条件分岐 条件分岐 let testFunc a b = if (a > b) then a else b If 式自身も値を返す構文であることに注目 そのためにelseは必須
42. 関数/条件分岐 If節の中に複数の命令を入れる場合は注意が必要 条件分岐 let testFunc a b = if (a > b) then (print_string “a > b”; a) else (print_string “a =< b”; b) 括弧の中を左から評価して最後の値を返す 注意:最後以外はunitを返さないとwarning ()の代わりにbegin … endでもOK
44. 繰り返し 繰り返しは再帰で書く.これ,最強. コンパイラが最適化しやすい 再帰する場合は”let” -> “let rec”で書きます let rectestFun a = if (a < 1) then 0 else a + testFun (a-1) しかし再帰ってスタックオーバーフローするんじゃ…? ↑ > 末尾再帰(tail recursion)すれば大丈夫です
46. 末尾再帰(tail recursion)の勧め 関数型言語の場合,末尾再帰の呼出し最適化によって再帰呼びだしは最適化されてジャンプ命令になる->スタック使わない よってスタックの増減は再帰回数に依存して増加しない 返すべき値を引数に渡すと案外簡単に末尾再帰に let testFunc a = let rectestFun1 a b = if (a < 1) then b else testFun1 (a-1) (b+a) in testFun1 a 0 testFun1 10 0 testFun1 9 10 testFun1 8 19 … testFun1 0 55 末尾再帰! A = 10 A = 0 A = 10 testFun1 10 0 testFun1 0 55 55
47. 末尾再帰(tail recursion)の勧め let rectestFun a = if (a < 1) then 0 else a + testFun (a-1) # testFun 100000000;; Stack overflow during evaluation (looping recursion?). 非末尾再帰で一億 let rectestFun a b = if (a < 1) then b else testFun (a-1) (b+a) # testFun 1000000000;; - : int = 5000000050000000 末尾再帰で十億 末尾再帰を思い付く(->再帰脳)は関数型言語の壁の一つ OcamlだけではなくHaskell, Closureでも使えるスキルなので是非!
62. レコード レコード定義c C言語でいう構造体 # type pair_of_ints = { a : int; b : int; };; type pair_of_ints = { a : int; b : int; } # {a = 10; b = 20};; - : pair_of_ints = {a = 10; b = 20} # let p = {a = 10; b = 20};; val p : pair_of_ints = {a = 10; b = 20} # p.a;; - : int = 10 # let {a=k; b=l} = p;; val k : int = 10 val l : int = 20 書けば型は推論 取るときは”.”を使って こうやってもOK
63. バリアント 関数型言語ならではの構造体 イメージとしてはCのenum + union typedefstructfoobar_ { enum type_ { TEST_VOID, TEST_INT, TEST_FLOAT, TEST_CHAR } type; union { inti; float d; char c; } val; } foobar; # type foobar = Void | Int of int | Float of float | Char of char | Pair of (int * int);; type foobar = Void | Int of int | Float of float | Char of string | Pair of (int * int) 非常にシンプル!
64. バリアント # type foobar = Void | Int of int | Float of float | Char of string | Pair of (int * int);; type foobar = Void | Int of int | Float of float | Char of string | Pair of (int * int) # Void;; - : foobar = Void # Int 20;; - : foobar = Int 20 # Pair (20, 20);; - : foobar = Pair (20, 20) 使用例: option # None;; - : 'a option = None # Some 10;; - : int option = Some 10
65. パターンマッチ データのパターン(型の構造)で条件分岐が可能! let foobarToStr = function Void -> "void" |Inti-> "Int(" ^ (string_of_inti) ^ ")" | Float f -> "Float(" ^ (string_of_float f) ^ ")" | Char c -> "Char(" ^ c ^ ")" | Pair (a, b) -> "Pair(" ^ (string_of_int a) ^ ", " ^ (string_of_int b) ^ ")";; valfoobarToStr : foobar -> string = <fun> int -> stringの変換:string_of_int 文字列の結合: ^ # foobarToStr Pair(10,20);; This function is applied to too many arguments, maybe you forgot a `;' # foobarToStr (Pair(10,20));; - : string = "Pair(10, 20)" # foobarToStr Void;; - : string = "void"
66. パターンマッチ パターンマッチの方法は3種類 # let p = Pair(10,20);; val p : foobar = Pair (10, 20) # let Pair(a,b) = p;; Warning P: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: (Char _|Float _|Int _|Void) val a : int = 10 val b : int = 20 # let p = (10,20);; val p : int * int = (10, 20) # let (a, b) = p;; val a : int = 10 val b : int = 20 # let (a, _) = p;; val a : int = 10 # 単に値を取る場合 しかしやや強引 ペア等には有効 “_”で値を無視
67. パターンマッチ # let foobarToStr a = match a with Void -> "Void" | Int a when a > 100 -> "BigInt" | Int _ -> "Int" | _ -> "Others";; valfoobarToStr : foobar -> string = <fun> # foobarToStr (Int 10);; - : string = "Int" # foobarToStr (Int 200);; - : string = "BigInt" # let foob# let foobarToStr a = match a with Void -> "Void" | Int _ -> "Int" | Int a when a > 100 -> "BigInt" | _ -> "Others";; Warning U: this match case is unused. valfoobarToStr : foobar -> string = <fun> # foobarToStr (Int 200);; - : string = "Int" When で 条件追加 比較は上から順番なのでこの場合BigIntが無視
68. 多相型データ・関数 ペアの右を返す関数 # let getRight(_, a) = a;; valgetRight : 'a * 'b -> 'b = <fun> ‘a , ‘b : “型”の変数で’どんな型が来ても構わない’ということ. ‘a と’bが同じでも構わない.ただし一回目の’bと二回目の’bは同じ # let getHead (h::t) = h;; Warning P: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: [] valgetHead : 'a list -> 'a = <fun> # let getHead l = match l with (h::t) -> Some h | [] -> None;; valgetHead : 'a list -> 'a option = <fun> どんな型のリストも対応する関数
69. 多相型データ・関数 多相型レコード # type 'a pairs = { a : 'a ; b : int; };; type 'a pairs = { a : 'a; b : int; } # type 'a tree = None | Node of ('a * 'a tree * 'a tree);; type 'a tree = None | Node of ('a * 'a tree * 'a tree) # Node(10, Node(20, None, None), Node(30, None, None));; - : int tree = Node (10, Node (20, None, None), Node (30, None, None)) # 多相型バリアントで バイナリツリー 10 20 30