SlideShare a Scribd company logo
数学プログラムを

Haskell で書くべき6の理由
早稲田大学基幹理工学部

数学科四年 石井大海
自己紹介
石井大海(いしい・ひろみ)
早稲田大学数学科四年(楫元研究室)
専門:集合論、計算機代数
四月から筑波の数学研究科に進学
塩谷研・照井研
発表内容
純粋関数型言語 Haskell の紹介
今年一年間 Haskell で計算機代数ライブラリを
書いてきた経験を踏まえて
実装や言語の詳細は後程捕まえて いて下さい
純粋関数型言語 Haskell
純粋関数型言語?

Haskell??
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
要するに?
Haskell は

記述力が凄い
Haskell は

記述力が凄い
Haskell は

記述力が凄い
何がどのように?
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
より数学らしいプログラム
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
より数学らしいプログラム
簡潔な記述・見掛け上無限を扱える
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
より数学らしいプログラム
簡潔な記述・見掛け上無限を扱える
安全性の保証・数学的構造の抽象化
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
より数学らしいプログラム
簡潔な記述・見掛け上無限を扱える
安全性の保証・数学的構造の抽象化
アルゴリズムに適した記述が可能
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
より数学らしいプログラム
簡潔な記述・見掛け上無限を扱える
安全性の保証・数学的構造の抽象化
アルゴリズムに適した記述が可能
効率的なプログラムの記述
宣言的・純粋関数型
宣言的・純粋関数型
C, Ruby……命令的・手続型
何をどうするか記述する
(命令する)
ループが基本
def fib(n)
a = b = 1
for i in 1..n
a, b = b, a + b
end
return a
end
宣言的・純粋関数型
C, Ruby……命令的・手続型
何をどうするか記述する
(命令する)
ループが基本
Haskell:宣言的、関数型
何がどうであるか記述する
(宣言する)
再帰が基本
def fib(n)
a = b = 1
for i in 1..n
a, b = b, a + b
end
return a
end
fib :: Int → Int → Int
fib 0 = 1
fib 1 = 1
fib n = fib (n-2) + fib (n-1)
宣言的・純粋関数型
C, Ruby……命令的・手続型
何をどうするか記述する
(命令する)
ループが基本
Haskell:宣言的、関数型
何がどうであるか記述する
(宣言する)
再帰が基本
def fib(n)
a = b = 1
for i in 1..n
a, b = b, a + b
end
return a
end
fib :: Int → Int → Int
fib 0 = 1
fib 1 = 1
fib n = fib (n-2) + fib (n-1)
宣言型の方が数学の「定義」に近い
代数的データ型
Haskell における共用体・構
造体(のようなもの)
既存の型の直積・直和で表
現
再帰的な定義も余裕
数学の定義をそのまま書き起
こせる
data Bool = False | True
data List a = Nil
| Cons a (List a)
!
data Expr
= Num Integer
| Neg Expr
| Plus Expr Expr
| Mult Expr Expr
| If Expr Expr Expr
パターンマッチ
帰納的に定義した型の値を、その形によって場合分
けできる
再帰的にデータを走査する際に便利
例:構文木の評価
eval :: Expr -> Int
eval (Num i) = i
eval (Neg i) = negate (eval i)
eval (Plus n m) = eval n + eval m
eval (Mult n m) = eval n * eval m
eval (If p t f) = if p == 0 then f else t
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
より数学らしいプログラム
簡潔な記述・見掛け上無限を扱える
安全性の保証・数学的構造の抽象化
アルゴリズムに適した記述が可能
効率的なプログラムの記述
遅延評価
Haskell は必要になるまで値を評価しない
C, Ruby の && や ¦¦ の振舞いに似ている
これらは第一引数の値で結果が決まる場合、
第二引数は評価しない
Haskell ではデフォルトで全てがこの評価戦略
遅延評価の例
可算無限の対象を扱える(列挙できるなら)
下:素数から成る無限リスト(注:これはとても非効率)
全部読もうとすると当然止まらないが、一部分だけ使う分
には問題ない
sieve [] = []
sieve (n : ns) =
n : sieve [ m | m ← ns, m `mod` n ≠ 0]
!
primes = 2 : sieve [3, 5 ..]
!
primesUpto100 = takeWhile (≦ 100) primes
遅延評価の応用例
有限次元代数とは限らない多項式環の剰余環
イデアルが零次元なら剰余環は有限次元、有限
個の元の乗算表を用意すれば良い
➡ 乗算表の値を必要になるまで遅延しておけば、
無限次元であっても扱うことが出来る!
遅延評価は思わぬメモリ漏れを引き起こしもする
ので、注意が必要
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
より数学らしいプログラム
簡潔な記述・見掛け上無限を扱える
安全性の保証・数学的構造の抽象化
アルゴリズムに適した記述が可能
効率的なプログラムの記述
強力な型システム(1)
静的型付き:コンパイル時に型をチェック(これはCも同じ)
Ruby など:実行時に型エラーで不正終了することがある
こうしたバグが防げるだけで、デバッグが格段に楽になる
型推論:コンパイラが型を推定してくれるので、大抵型を自分
で書かなくても大丈夫
型安全
型が付いたプログラムは(何かの意味で)絶対に刺さらない
C, C++, ... : 型安全ではない:cast や printf で簡単にSEGV
Haskell, OCaml:型安全である
型安全性と型の意味
そもそも型とは?
○ 値の種類を表すタグ
int は自然数、char は文字、[int] はint値のリスト etc, etc...
◎ 値や関数の不変条件の表明
実際に不変条件として振る舞うには、システムが型安全性を
満たす必要がある
Haskell の型は十分強力なので、配列の境界条件や変数条件
などを型の制約として書ける
安全性を静的に保証出来る!
強力な型システム(2)
型クラス(アドホック多相)
似た操作・演算を持つ型を抽象化出来る
オブジェクト指向の mix-in やトレイトに相当
応用例:有理数型や有限体型に対して個別に線型代数やグレブナー
基底計算を実装するのではなく、一般の体について実装出来る
class Field k where
zero :: k
one :: k
(+) :: k -> k -> k
(*) :: k -> k -> k
recip :: k -> Maybe k
強力な型システム(3)
多相型(パラメトリック多相)
リストや木など、中身に関係のないデータ構造
や関数を総称的に定義出来る
以下は二分木とその深さの例
data Tree a = Leaf a | Branch (Tree a) (Tree a)
!
depth :: Tree a -> Int
depth (Leaf _) = 0
depth (Branch t t') = 1 + max (depth t) (depth t')
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
より数学らしいプログラム
簡潔な記述・見掛け上無限を扱える
安全性の保証・数学的構造の抽象化
アルゴリズムに適した記述が可能
効率的なプログラムの記述
柔軟な副作用の制御
副作用とは?
Haskell は純粋関数型言語
Haskell の関数は全て数学的な意味での関数
純粋 = 同じ引数に対して同じ値を返す
最適化や並列化に有利・定義順を気にしなくてよい
副作用……関数の純粋性を破るような作用
入出力、乱数の生成、etc...
純粋だと何が嬉しいか
データの部分構造を共有出来る
関数の定義順を気にする必要がない
同じ計算を二度しなくて済む(答えが変わらな
いから)
Q. 副作用なしでどうやっ
てプログラムを書くの?
モナドによる副作用の制御
圏論のモナドの概念を使って副作用を抽象化する
副作用を持ち得る値はモナドに包んで扱う
型 a を持つけど何らかの意味で副作用を含むものをモナドで包んで
m a 型の値として区別する
代表的なモナド
入出力:IO、非決定性計算:リスト、失敗計算:Maybe、

破壊的代入:ST などなど
IO Int : 入出力を伴った整数、

[String]:文字列のリストまたは文字列を使った非決定性計算
モナドを理解せずとも

Haskell は使える
型レベルで副作用が区別されることが大事
IO モナドや ST モナドの場合、副作用を外に取り出すことは
出来ない
C とか Ruby のように、手続型っぽく副作用を書くことが出来る
自前の「モナド」を定義でき、DSL が手軽に作れる
使う副作用の種類を限定でき、型によって副作用を使う場所が区
別出来る
代入や破壊的変更によるバグを防げる
例:クィックソート
一般的に Haskell の例として出される qsort
実はこれは偽物
pivot してない、in-place じゃない、遅い……
qsort :: [Int] → [Int]
qsort [] = []
qsort (a : as) =
qsort [x | x ← xs, x ≦ a] ++ [a]
++ qsort [y | y ← ys, y > a]
例:クィックソート
破壊的変更による本物のクィックソート(抜粋)
(出典:Quicksort in Haskell)
qsort' l r =
if1 (r > l) $ do
i ← auto l
j ← auto (r+1)
let v = a[l] :: E m a
iLTj = i < (j :: E m i)
while iLTj $ do
while ((i += 1) < mub && a[i] < v)
skip
while (a[j -= 1] > v) skip
if1 iLTj $ a[i] =:= a[j]
a[l] =:= a[j]
qsort' l (j-1)
qsort' i r
副作用:まとめ
型により副作用と純粋な関数を分離できる
記述するアルゴリズムの性質に応じて、数学的
な関数として記述するか、破壊的変更や可変状
態を使って手続的に記述するかが選べる
手軽に DSL が作れる
Haskell の特徴
宣言的・純粋関数型
代数的データ型
遅延評価
強力な型システム
柔軟な副作用の制御
並列・並行処理が得意
より数学らしいプログラム
簡潔な記述・見掛け上無限を扱える
安全性の保証・数学的構造の抽象化
アルゴリズムに適した記述が可能
効率的なプログラムの記述
時間切れorz
今後の課題
数値計算は LAPACK などへの高レベルなバインディン
グがある
記号計算系のライブラリが圧倒的に不足
効率的な計算の為にはモジュラー計算や記号線型代
数のライブラリの開発が必要
効率的なプログラムを書くのにはコツが必要
どの言語でもそうだが、ノウハウの蓄積が必要
入門書
すごい Haskell 楽しく学ぼう!
他言語での経験がある人向けの、肩肘の張らない解説書の邦訳
関数プログラミング入門 Haskell で学ぶ原理と技法
Haskell を使って、関数型プログラミングの考え方を基礎から学ぶ良書。
型システム入門
型システムに関する定番教科書の邦訳。
この三冊にはレビュアーとして参加してたりします(自慢)
Purely Functional Data Structures
純粋関数型を生かしたデータ構造の解説書
まとめ
Haskell は数学的プログラムを記述するのに適す
宣言的にも手続的にも書ける
遅延評価による計算量の工夫
リッチな型システムが安全性や表現力を増加
詳細はシンポジウムで!

More Related Content

数学プログラムを Haskell で書くべき 6 の理由