PowerPoint

(Rubyistのための)
超音速:ML入門
福盛秀雄
http://fukumori.org
Ver.2005.07.30
1
準備運動
階乗の計算
関数名
引数
let rec factorial n =
if n = 1 then 1 else
factorial (n - 1) * n
2
こんな書き方も
let rec factorial = function
| 1 -> 1
| n -> factorial (n - 1) * n
引数の「パターン」を記述
3
並べてみる
let rec factorial n =
if n = 1 then 1 else
factorial (n - 1) * n
let rec factorial = function
| 1 -> 1
| n -> factorial (n - 1) * n
下の方が「ML的スタイル」
4
対話型環境を使ってみる
$ ocaml
Objective Caml version 3.08.3
#
5
ふつ~の計算
コロン二つで式の評価
# 123 + 456;;
- : int = 579
結果の表示
6
ふつ~の計算!?
浮動小数点の足し算は +.
# 123.0 +. 456.0;;
- : float = 579.
普通じゃないよ!
7
「暗黙の型変換」?
んなものは無い。
# 123.0 +. 456;;
Characters 9-12:
123.0 +. 456;;
^^^
浮動小数点演算と
整数演算を混ぜてみる
(当然のように)
エラーとなる
This expression has type int but is here used
with type float
8
これならOK
int -> floatの関数
# 123.0 +. float_of_int 456;;
- : float = 579.
9
変数の定義
let <変数名> = ...で定義
# let a = 1;;
val a : int = 1
# let x = “abc”;;
val x : string = “abc”
定義された変数の型が表示される
10
関数の定義
関数の定義もlet
関数名、引数の順に記述
# let f x = x + 1;;
val f : int -> int = <fun>
“int -> int”型の関数“f”が定義された
11
関数の定義(2)
複数の引数がある場合、
(OCamlでは通常)スペースで区切って並べる
# let g x y = x * y;;
val g : int -> int -> int = <fun>
これをなんと読む?
→「二つのintを順に受け取り、intを返す関数」
12
「関数」と「変数」の区別は?
本質的には両者の間に明確な区別はない(?)。
関数を別の変数に束縛したり、
別の関数の引数にしたりすることもできる
# let f x = x + 1
val f : int -> int = <fun>
変数gに
関数fを束縛
# let g = f;;
val g : int -> int = <fun>
# g 1;;
- : int = 2
13
無名関数
名前の通り「名前の無い関数」
Rubyのブロックに似ていないこともない
Ruby: {|x| x + 1}
OCaml: fun x -> x + 1
ちなみにOCamlにて
let f x = x + 1 と
let f = fun x -> x + 1 は等価
14
再帰を示す“rec”
再帰関数の定義には“rec”を付ける
# let rec factorial n =
if n = 1 then 1 else
factorial (n - 1) * n;;
val factorial : int -> int = <fun>
付けないと “Unbound value factorial”
というエラーとなる
15
関数型言語の特徴
# let counter = 0 ;;
変数への「代入」はできない
val counter : int = 0 左辺のcounterと右辺のcounterは
別のもの
# let count () =
let counter = counter + 1 in
counter;;
val count : unit -> int = <fun>
# count ();;
- : int = 1
# count ();;
- : int = 1
counterは常に0のため
count()の結果は常に1
16
関数型言語の特徴(2)
•変数はimmutable→代入はできない
•関数は同じ引数に対して必ず同じ値を返す
•ループは書けない(書かない)
→再帰を使う
•変数は変更できない
→計算の途中経過は引数と
返り値に入れておく
17
関数型言語の特徴(3)
「計算の途中経過は引数と
返り値に入れておく」
どうやって実現?
値の定義:強力なデータ型
値の参照:強力なパターンマッチング
18
リスト(list)とタプル(tuple)
リスト
;で区切る
# [1;2;3];;
- : int list = [1; 2; 3]
タプル
# (1,2,3);;
- : int * int * int = (1, 2, 3)
19
リストのつくりかた
[1;2;3]と書くほかにも…
要素とリストをコロン二つで連結
# 1::[2;3];;
- : int list = [1; 2; 3]
[]は空リスト
# 1::2::3::[];;
- : int list = [1; 2; 3]
20
レコード型
レコード型“rt”を定義
“rt”はメンバー
“a”,”b”を持つ
# type rt = {a : int; b : string};;
type rt = { a : int; b : string; }
# let rv = {a=1; b="xyz"};;
val rv : rt = {a = 1; b = "xyz"}
“rt”型の変数“rv”が定義された
21
ヴァリアント型
“ Cのenum”的な使い方
# type vt = Apple | Banana | Orange;;
type vt = Apple | Banana | Orange
# let vv = Apple;;
val vv : vt = Apple
22
ヴァリアント型(2)
値つきのヴァリアント型を定義
# type vt2 = Ival of int | Fval of float;;
type vt2 = Ival of int | Fval of float
# let vvi = Ival 0;;
val vvi : vt2 = Ival 0
23
パターンマッチング
整数値に対するパターンマッチの例
let rec factorial = function
| 1 -> 1
| n -> factorial (n - 1) * n
24
パターンマッチング(2)
リストに対するパターンマッチの例
“Pretty Print List” - 「文字列のリスト」を「文字列」へ変換
let rec pp_list = function
xはリストの先頭
| [] -> ""
xsはリストの残り
^は文字列の連結
| [x] -> x
| x :: xs -> x ^ " " ^ pp_list xs
# pp_list;;
- : string list -> string = <fun>
# pp_list [“str1”;”str2”;”str3”];;
- : string = “str1 str2 str3”
25
パターンマッチング(3)
ヴァリアントに対するパターンマッチの例
UnSafeはサニタイズされていないHTML文字列
Safeはサニタイズ済みのHTML文字列(のつもり)
type htmlstr =
| UnSafe of string
| Safe of string
二つの引数に対する
パターンマッチング
let concat h1 h2 =
match h1, h2 with
| UnSafe(s1), UnSafe(s2) -> UnSafe(s1 ^ s2)
| UnSafe(s1), Safe(s2) -> UnSafe(s1 ^ s2)
| Safe(s1), UnSafe(s2) -> UnSafe(s1 ^ s2)
| Safe(s1), Safe(s2) -> Safe(s1 ^ s2)
26
モジュール
『超』乱暴な説明:
Ruby:
module Trig
PI = 3.141592654
def Trig.sin(x)
# ..
end
def Trig.cos(x)
# ..
end
end
Trig.cos(0)
=> 1.0
OCaml:
module Trig =
struct
let pi = 3.141592654
let sin x =
...
let cos x =
...
end
# Trig.cos 0.0;;
- : float = 1.
27
標準ライブラリ
Listモジュールが
特によく使われるのでとりあえず紹介:
名前の通りリスト関連の
関数が定義されている
28
List.map
Rubyの“collect”イテレータと『ほぼ』同じ(?)
Ruby:
[1,2,3].collect {|x| x + 1}
=> [2, 3, 4]
OCaml:
# List.map (fun x -> x + 1) [1;2;3];;
- : int list = [2; 3; 4]
29
List.fold_left
Rubyの“inject”イテレータと『ほぼ』同じ(?)
Ruby:
[1,2,3].inject(0) {|sum, element| sum + element}
=> 6
OCaml:
# List.fold_left
(fun sum element -> sum + element) 0 [1;2;3];;
- : int = 6
30
命令型処理
“ref”で代入可能な変数(参照型変数)を定義
# let counter = ref 0 ;;
val counter : int ref = {contents = 0}
# let count () =
counter := !counter + 1; ;でつなげることにより
!counter ;;
複数の式を順に評価
# count ();;
- : int = 1
# count ();;
- : int = 2
• :=で代入(letが無いことに注意)
• !を付けると参照型変数の
実際の値が得られる
31
他にもいろいろありますが…
あとは実践あるのみ。
MinCamlのソースコードを
読みに行きましょう。
ということで一旦お開き
32