Más contenido relacionado La actualidad más candente (20) Similar a 純粋関数型アルゴリズム入門 (20) Más de Kimikazu Kato (20) 純粋関数型アルゴリズム入門6. 今日の話
この本の解説 Purely Functional
Data Structure
Chris Okazaki
リストとキューの話しかしません
(それでも十分難しい)
7. 関数型言語の利点
• 本を読め! by C.Okazaki
– J.Backus 1978
– J.Hughes 1989
– P.Hudak and M.P. Jones 1994
• それだとあんまりなので、適宜説明しま
す
9. 純粋関数型データ構造
(アルゴリズム)
• データの永続性が保証されるデータ構造
(アルゴリズム)である
• つまり永続性による保守性と、計算効率
を同時に考慮する
10. 例:リストの結合
xs=[1; 2; 3; 4]
ys=[5; 6; 7; 8]
zs=xs++ys
手続き型言語(たとえばC言語)では?
xs ys
1 2 3 4 5 6 7 8
zs
これだと、実行後xsの値が書き変わってしまう
(永続的ではない)
永続性の保証のためには、事前にデータのコピーが必要
11. 純粋関数型アルゴリズム
すべてをコピーする必要はない!
xs ys
1 2 3 4 5 6 7 8
xs’
1 2 3 4
片方だけコピーすればよい
zs
通常 純粋関数型
計算 O(1) O(n)
量:
損している?→もっといい方法がある(後で説明)
12. 純粋関数型アルゴリズムを
学ぶメリット
• (非純粋)関数型言語を使っている人:
一部を純粋関数型にすることで保守性を
高めることができる
• 純粋関数型言語を使っている人:計算効
率の良いプログラムが書ける
• 手続き型言語を使っている人:保守性を
高めることができる
– 解く問題によっては・・・
14. 準備:リストの基本操作
a=[1;2;3]
head a => 1 a 1 2 3
1
b=tail a => [2;3]
a 1 2 3
b
c=Cons(0,a) => [0;1;2;3]
c 0 1 2 3
a
すべて永続性を保持しても計算量O(1)
15. ならし計算量
(Amortized Complexity)
• 一連の操作の中で、ならして(平均的
な)計算量を考える
• 一連の操作の中で一部計算量が多いこと
があったとしても、平均すると計算量が
小さいということがある
17. 純粋関数型なキューのアルゴリズ
ム
• キューを2つのリストの組(f,r)として表す
– ただし、常にfが空にならないように管理する
– fが空になった時は、(reverse r, [])を返す
• 新しく値を加える(snoc)ときは、rの頭
に加える
• 値を取得する(head)ときは、fの頭の値
を取得する
• 値を削除する(tail)ときは、fの頭を削除
する
18. 通常のリストによる表現 純粋関数型データ構造
q1=snoc (empty,1)
([], [1])
[1]
([1], [])
q2=snoc (q1,2)
q3=snoc (q2,3)
q4=snoc (q3,4)
[1;2;3;4] ([1], [4;3;2])
q5=tail q4
([], [4;3;2])
[2;3;4]
([2;3;4], [])
q6=tail q5
[3;4] ([3;4], [])
q7=snoc q6 5
[3;4;5] ([3;4], [5])
21. ならし計算量
• snocの計算量は2だと思う(実際には1なの
で、1貯金する)
• tailの計算量は
– 通常は1
– fの要素数が1のときは貯金を使って1(rのサ
イズがmになるまではsnocがm回行われている
→貯金はmある)
• つまり、ならせばすべての操作の計算量
はO(1)であるとみなしてよい
25. 記法
• 既存関数を遅延評価するときは頭に$を付
ける
• 遅延評価する関数を新たに定義するとき
は、関数名の前にlazyを付ける
• 強制的に評価するときには、force関数を
適用 例:n番目の素数を返す関数primes n
val p=$primes 1000000 一瞬で終わる(計算しない)
val q=force p 時間がかかる
val r=force p 一瞬で終わる(答を覚えている)
26. 遅延付きリスト(ストリーム)
~アイデア~
• リストの結合は最終的な目的ではない
• 通常リストについての操作で最終的な目
的は、リストの一部を表示または他の計
算に使うこと
• 以下、2つのリストを結合した後にtake k
(最初のk個の要素をとる)をするという
ストーリを考える
27. リストはConsの遅延で表現する
val p=$Cons(1,$Cons(2,$Cons(3,$Cons(4,$Nil))))
1 2 3 4
val q=$Cons(4,$Cons(5,$Cons(6,$Cons(7,$Nil))))
4 5 6 7
fun lazy ($Nil)++t=t
| ($Cons(x,s))++t=$Cons(x,s++t)
fun lazy take(0,s) = $Nil
| take(n,$Nil) = $Nil
| take(n,$Cons(x,s)) = $Cons(x,take(n-1,s))
28. fun lazy ($Nil)++t=t
| ($Cons(x,s))++t=$Cons(x,s++t)
fun lazy take(0,s) = $Nil
| take(n,$Nil) = $Nil
| take(n,$Cons(x,s)) = $Cons(x,take(n-1,s))
val p=$Cons(1,$Cons(2,$Cons(3,$Cons(4,$Nil))))
val q=$Cons(4,$Cons(5,$Cons(6,$Cons(7,$Nil))))
val r=take(2,p++q) val s=force r
take(2, $Cons(1,$Cons(2,$Cons(3,$Cons(4,$Nil))))
++$Cons(4,$Cons(5,$Cons(6,$Cons(7,$Nil)))))
=> take(2, $Cons(1,$Cons(2,$Cons(3,$Cons(4,$Nil)))
++$Cons(4,$Cons(5,$Cons(6,$Cons(7,$Nil))))))
=> $Cons(1,take(1,$Cons(2,$Cons(3,$Cons(4,$Nil)))
++$Cons(4,$Cons(5,$Cons(6,$Cons(7,$Nil))))))
=> $Cons(1,take(1,$Cons(2,$Cons(3,$Cons(4,$Nil))
++$Cons(4,$Cons(5,$Cons(6,$Cons(7,$Nil)))))))
=> $Cons(1,$Cons(2,take(0,$Cons(3,$Cons(4,$Nil))
++$Cons(4,$Cons(5,$Cons(6,$Cons(7,$Nil)))))))
=> $Cons(1,$Cons(2,$Nil)
31. 基本的アイデア
• reverseの遅延評価版「rotate」を考える
• rのサイズがfのサイズより1大きいときに
限ってrotateが動くこととする
– 非遅延版はf=[]のときにreverseが走った
– 今度はf++reverse rを計算したい
• しかしそのまま直接では、遅延評価版を
作れないので、アキュムレータを入れる
(f,r,a)のタプルを考える
fun rotate ($Nil, Cons(y,_), a)=$Cons(y,a)
| rotate ($Cons(x,xs), Cons(y,ys), a) 注:rについては非遅延リストでいい
=$Cons(x,rotate(xs,ys,$Cons(y,a)))
32. 例 fun rotate ($Nil, Cons(y,_), a)=$Cons(y,a)
| rotate ($Cons(x,xs), $Cons(y,ys), a)
=$Cons(x,rotate(xs,ys,$Cons(y,a)))
f=[1;2], r=[5;4;3] の場合
f2=rotate($Cons(1,$Cons(2,$Nil)), [5;4;3], $Nil)
=> $Cons(1, rotate($Cons(2,$Nil),[4;3],$Cons(5,$Nil))) ここで停止する
次にtailをとると・・・
f3=tail f2
=>rotate($Cons(2,$Nil),[4;3],$Cons(5,$Nil))
=>$Cons(2, rotate($Nil,[3],$Cons(4,$Cons(5,$Nil)))) ここで停止する
またまたtailをとると・・・
f4=tail f3
=>rotate($Nil,[3],$Cons(4,$Cons(5,$Nil)))
=>$Cons(3,$Cons(4,$Cons(5,$Nil)))
ここで停止する
以下tailをとる操作は自明
以上のようなrotateを部品として使う
ほかにも工夫が必要だが、説明略
33. まとめ
• 純粋関数型アルゴリズムは便利
– 計算は速いし、保守性も高い
– もちろん多少の犠牲(オーバーヘッド)は伴
う
• 純粋関数型アルゴリズムを設計するうえ
で遅延評価は重要
– 計算量に直接効いてくる
– ならし計算量がならさなくてもよくなる
(deamortalization)