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

46
数学プログラムを Haskell で書くべき6の理由 早稲田大学基幹理工学部 数学科四年 石井大海

Upload: hiromi-ishii

Post on 24-May-2015

15.052 views

Category:

Technology


1 download

DESCRIPTION

筑波大学計算機数学グループ春の館山合宿での講演「数学プログラムを
Haskell で書くべき6の理由」の発表資料。実際の講演映像は https://www.youtube.com/watch?v=S4_7KVNA-Ww

TRANSCRIPT

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

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

早稲田大学基幹理工学部数学科四年 石井大海

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

自己紹介石井大海(いしい・ひろみ)

早稲田大学数学科四年(楫元研究室)

専門:集合論、計算機代数

四月から筑波の数学研究科に進学

塩谷研・照井研

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

発表内容

純粋関数型言語 Haskell の紹介

今年一年間 Haskell で計算機代数ライブラリを書いてきた経験を踏まえて

実装や言語の詳細は後程捕まえて訊いて下さい

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

純粋関数型言語 Haskell

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

純粋関数型言語?Haskell??

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

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

要するに?

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

Haskell は記述力が凄い

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

Haskell は記述力が凄い

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

Haskell は記述力が凄い

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

何がどのように?

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

より数学らしいプログラム

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

より数学らしいプログラム

簡潔な記述・見掛け上無限を扱える

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

より数学らしいプログラム

簡潔な記述・見掛け上無限を扱える

安全性の保証・数学的構造の抽象化

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

より数学らしいプログラム

簡潔な記述・見掛け上無限を扱える

安全性の保証・数学的構造の抽象化

アルゴリズムに適した記述が可能

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

より数学らしいプログラム

簡潔な記述・見掛け上無限を扱える

安全性の保証・数学的構造の抽象化

アルゴリズムに適した記述が可能

効率的なプログラムの記述

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

宣言的・純粋関数型

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

宣言的・純粋関数型C, Ruby……命令的・手続型

何をどうするか記述する(命令する)

ループが基本

def fib(n) a = b = 1 for i in 1..n a, b = b, a + b end return a end

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

宣言的・純粋関数型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)

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

宣言的・純粋関数型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)

宣言型の方が数学の「定義」に近い

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

代数的データ型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

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

パターンマッチ帰納的に定義した型の値を、その形によって場合分けできる

再帰的にデータを走査する際に便利

例:構文木の評価

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

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

より数学らしいプログラム

簡潔な記述・見掛け上無限を扱える

安全性の保証・数学的構造の抽象化

アルゴリズムに適した記述が可能

効率的なプログラムの記述

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

遅延評価Haskell は必要になるまで値を評価しない

C, Ruby の && や || の振舞いに似ている

これらは第一引数の値で結果が決まる場合、第二引数は評価しない

Haskell ではデフォルトで全てがこの評価戦略

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

遅延評価の例可算無限の対象を扱える(列挙できるなら)

下:素数から成る無限リスト(注:これはとても非効率)

全部読もうとすると当然止まらないが、一部分だけ使う分には問題ない

sieve [] = [] sieve (n : ns) = n : sieve [ m | m ← ns, m `mod` n ≠ 0] !primes = 2 : sieve [3, 5 ..] !primesUpto100 = takeWhile (≦ 100) primes

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

遅延評価の応用例有限次元代数とは限らない多項式環の剰余環

イデアルが零次元なら剰余環は有限次元、有限個の元の乗算表を用意すれば良い

➡ 乗算表の値を必要になるまで遅延しておけば、無限次元であっても扱うことが出来る!

遅延評価は思わぬメモリ漏れを引き起こしもするので、注意が必要

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

より数学らしいプログラム

簡潔な記述・見掛け上無限を扱える

安全性の保証・数学的構造の抽象化

アルゴリズムに適した記述が可能

効率的なプログラムの記述

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

強力な型システム(1)静的型付き:コンパイル時に型をチェック(これはCも同じ)

Ruby など:実行時に型エラーで不正終了することがある

こうしたバグが防げるだけで、デバッグが格段に楽になる

型推論:コンパイラが型を推定してくれるので、大抵型を自分で書かなくても大丈夫

型安全

型が付いたプログラムは(何かの意味で)絶対に刺さらない

C, C++, ... : 型安全ではない:cast や printf で簡単にSEGV

Haskell, OCaml:型安全である

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

型安全性と型の意味そもそも型とは?

○ 値の種類を表すタグ

int は自然数、char は文字、[int] はint値のリスト etc, etc...

◎値や関数の不変条件の表明

実際に不変条件として振る舞うには、システムが型安全性を満たす必要がある

Haskell の型は十分強力なので、配列の境界条件や変数条件などを型の制約として書ける

安全性を静的に保証出来る!

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

強力な型システム(2)型クラス(アドホック多相)

似た操作・演算を持つ型を抽象化出来る

オブジェクト指向の mix-in やトレイトに相当

応用例:有理数型や有限体型に対して個別に線型代数やグレブナー基底計算を実装するのではなく、一般の体について実装出来る

class Field k where zero :: k one :: k (+) :: k -> k -> k (*) :: k -> k -> k recip :: k -> Maybe k

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

強力な型システム(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')

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

より数学らしいプログラム

簡潔な記述・見掛け上無限を扱える

安全性の保証・数学的構造の抽象化

アルゴリズムに適した記述が可能

効率的なプログラムの記述

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

柔軟な副作用の制御副作用とは?

Haskell は純粋関数型言語

Haskell の関数は全て数学的な意味での関数

純粋 = 同じ引数に対して同じ値を返す

最適化や並列化に有利・定義順を気にしなくてよい

副作用……関数の純粋性を破るような作用

入出力、乱数の生成、etc...

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

純粋だと何が嬉しいか

データの部分構造を共有出来る

関数の定義順を気にする必要がない

同じ計算を二度しなくて済む(答えが変わらないから)

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

Q. 副作用なしでどうやってプログラムを書くの?

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

モナドによる副作用の制御圏論のモナドの概念を使って副作用を抽象化する

副作用を持ち得る値はモナドに包んで扱う

型 a を持つけど何らかの意味で副作用を含むものをモナドで包んで m a 型の値として区別する

代表的なモナド

入出力:IO、非決定性計算:リスト、失敗計算:Maybe、 破壊的代入:ST などなど

IO Int : 入出力を伴った整数、[String]:文字列のリストまたは文字列を使った非決定性計算

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

モナドを理解せずともHaskell は使える

型レベルで副作用が区別されることが大事

IO モナドや ST モナドの場合、副作用を外に取り出すことは出来ない

C とか Ruby のように、手続型っぽく副作用を書くことが出来る

自前の「モナド」を定義でき、DSL が手軽に作れる

使う副作用の種類を限定でき、型によって副作用を使う場所が区別出来る

代入や破壊的変更によるバグを防げる

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

例:クィックソート一般的に 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]

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

例:クィックソート破壊的変更による本物のクィックソート(抜粋)

(出典: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

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

副作用:まとめ

型により副作用と純粋な関数を分離できる

記述するアルゴリズムの性質に応じて、数学的な関数として記述するか、破壊的変更や可変状態を使って手続的に記述するかが選べる

手軽に DSL が作れる

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

Haskell の特徴宣言的・純粋関数型

代数的データ型

遅延評価

強力な型システム

柔軟な副作用の制御

並列・並行処理が得意

より数学らしいプログラム

簡潔な記述・見掛け上無限を扱える

安全性の保証・数学的構造の抽象化

アルゴリズムに適した記述が可能

効率的なプログラムの記述

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

時間切れorz

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

今後の課題数値計算は LAPACK などへの高レベルなバインディングがある

記号計算系のライブラリが圧倒的に不足

効率的な計算の為にはモジュラー計算や記号線型代数のライブラリの開発が必要

効率的なプログラムを書くのにはコツが必要

どの言語でもそうだが、ノウハウの蓄積が必要

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

入門書すごい Haskell 楽しく学ぼう!

他言語での経験がある人向けの、肩肘の張らない解説書の邦訳

関数プログラミング入門 Haskell で学ぶ原理と技法

Haskell を使って、関数型プログラミングの考え方を基礎から学ぶ良書。

型システム入門

型システムに関する定番教科書の邦訳。

この三冊にはレビュアーとして参加してたりします(自慢)

Purely Functional Data Structures

純粋関数型を生かしたデータ構造の解説書

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

まとめHaskell は数学的プログラムを記述するのに適す

宣言的にも手続的にも書ける

遅延評価による計算量の工夫

リッチな型システムが安全性や表現力を増加

詳細はシンポジウムで!