狠狠撸
Submit Search
蹿颈产蝉を読む
?
2 likes
?
868 views
Sosuke MORIGUCHI
Follow
Haskellにおいて、よく遅延評価の説明代わりに使われるフィボナッチ数列の定義について説明。 意図と意味は別物。
Read less
Read more
1 of 34
Download now
More Related Content
蹿颈产蝉を読む
1.
蹿颈产蝉を読む ~意図と意味を分ける~ chiguri
2.
大仰なタイトル ?これはHaskellにおけるフィボナッチ数列の定義 >
fibs = 0:1:zipWith (+) fibs (tail fibs) について、 ?今からなにを説明するのか ?どの順で説明するのか ?を考えながら説明するものです。 ?詳しい人ほど、全部いっぺんに話しますが、私の 頭はそんなにメモリはありません。順序よく。
3.
注意 ?n番目、については基本0-originで行きます。 ?別に1-originでもいいですけど、些末事です。
?そんなことにこだわるくらいならこのスライド閉じま しょう。
4.
まず ?愚直なn番目のフィボナッチ数を計算するコード fib(0)
= 0 fib(1) = 1 fib(n+2) = fib(n)+fib(n+1) を書いておきます。 ?0番目は0、1番目は1、2番目以降は2つ前と1つ前 を足したものです。 ?このコード、愚直にこのまま実装すると当然遅いです が、これが大元です。まずは定義に立ち返る。
5.
fibsの定義 ?fibsの定義は、関数じゃなくてリストです。 fibs
= 0:1:zipWith (+) fibs (tail fibs) ?「fibsは0番目は0、1番目は1と続いて、残りはfibsと fibsの一つ後ろの列を足し算で合わせた残り」 ?再帰的なのでちょっとぴんときません。 「なんでfibsがそこで使えるの?」といったところです。
6.
fibsと、それの指す内容 ?fibsはフィボナッチ数列であるという意図です。 fibs
= 0:1:zipWith (+) fibs (tail fibs) ?もっというと、fibsは「フィボナッチ数を0番目か ら並べたもの」です。 ?とりあえずfibsはそういうものだ、と思うことに します。
7.
フィボナッチ数を 1番目から並べたもの ?fibsの意図からすると、1番目から並べたものは次
のものです。 1:zipWith (+) fibs (tail fibs) = tail fibs ?fibsの先頭を省いただけです。 ?fibsがもし適切にフィボナッチ数を0番目から並べ たものであれば、これは確かに1番目から並べたも のです。
8.
2番目から並べたもの ?当然ですが、2番目から並べたものは次の通り。 zipWith
(+) fibs (tail fibs) ?fibsとtail fibsの意図は先ほど説明しました。 ?fibs:フィボナッチ数を0番目から並べたもの ?tail fibs:フィボナッチ数を1番目から並べたもの ?つまり、「2番目から並べたもの」は、「0番目か ら並べたもの」と「1番目から並べたもの」を「足 して並べたもの」になります。
9.
大まかな図 0 1
1 2 3 5 8 1 1 2 3 5 8 1 2 3 5 8
10.
フィボナッチ数の定義とすると 0 1
1 2 3 5 8 1 1 2 3 5 8 1 2 3 5 8 2つ前 1つ前 2番目以降
11.
定義そのまま 0 1
1 2 3 5 8 1 1 2 3 5 8 1 2 3 5 8 fib(n) fib(n+1) fib(n+2)
12.
?計算過程は、こんなつたない図よりも、 @fumieval さんの書いた図
https://twitter.com/fumieval/status/529973251875688448 の方が断然わかりやすいです。 ?是非見ましょう。 ?最初の二つが定義されれば、次の要素は前二つから計 算できるので、全体が定義できる、ということがわか りやすいです。
13.
意図から記号へ ?fibsの定義を、記号的にいじってみます。 fibs
= 0:1:zipWith (+) fibs (tail fibs) ?右辺のfibsを定義によって一段階剥がしてみます。 0:1:zipWith (+) (0:1:zipWith (+) fibs (tail fibs)) (tail (0:1:zipWith (+) fibs (tail fibs))) = 0:1:zipWith (+) (0:1:zipWith (+) fibs (tail fibs)) (1:zipWith (+) fibs (tail fibs)) = 0:1:1:zipWith (+) (1:zipWith (+) fibs (tail fibs)) (zipWith (+) fibs (tail fibs))
14.
fibsの数の増え方がヤバい ?最初の定義には二つ、一段階展開すると四 つ???
?足し算もzipWithをやったものを足してる。 ?元の定義通りだと思えば、当たり前といえば当た り前。 ?展開する、というのは関数の再帰呼び出しみたいなも のだから。
15.
浮かぶ疑問 ?Q:遅くないの?計算したら無限ループとかにな らないの?
?A:再帰的定義だけど、リストの前から順に計算し て必要なところまで進めるから大丈夫です。 ?前から順に計算する部分が@fumieval さんの図
16.
遅延評価 ?一般にHaskellの処理系は遅延評価を採用している。 ?しなくてもいいらしいけど、してる。
?fibsの5番目が必要なら、「前から順に」5番目ま で計算する。 ?残りは「続きはこんな計算」と覚えておき、必要にな ったら計算する。 =計算を遅延させる。
17.
遅延評価でのfibsの動き ?fibsは定義の中でfibsを使っているけれど、fibsが 指しているのは値なので、右辺で使われたfibsは
計算で作られた値を指しています。 ?残りの計算では次の値を取り出して、の繰り返し。 ?以下、計算過程を図で表示します。 なお、図中では、リストを下のように表します。 ?0:1:?(何かリストが続く)を表している。 0 1 ?
18.
fibsの作られる過程 fibの定義 0
fibs 1 zipWith (+) fibs (tail fibs)
19.
fibsの作られる過程 fibsをその値で書き換える 0
fibs 1 zipWith (+) (tail )
20.
fibsの作られる過程 tailの計算:リストを一つ進める 0
fibs 1 zipWith (+)
21.
fibsの作られる過程 値を取り出し、矢印を進める 0
fibs 1 zipWith (+) (0: ) (1: )
22.
fibsの作られる過程 zipWithを進めてリストを作る 0
fibs 1 zipWith (+) 1
23.
fibsの作られる過程 値を取り出し、矢印を進める 0
fibs 1 1 zipWith (+) (1: ) (1: )
24.
fibsの作られる過程 zipWithを進めてリストを作る 0
fibs 1 1 zipWith (+) 2
25.
後は分かるね? ?もう次は想像できましたね?次に1と2が取り出さ れて矢印を進めて、???を繰り返します。
?つまり、実際の「次の値」の計算はzipWith (+)に よる計算一回分に過ぎませんから、計算量として は全然大したことがないわけです。 ?単純な繰り返し+α程度。 ?意図通り、二つ前の要素と一つ前の要素を見るように うまく定義されている。
26.
終わりに ?今回は、fibsの定義について ?どういう考えで作られているかという「意図」と
?どう動くのかという「意味」と を分けて説明しました。 ?記号を相手にするときは、「意図」と「意味」を 分けながら考えましょう。 ?意図を表せていないならどうしようもない。 ?意味がそぐわないなら、どうそぐわないかを考える。
27.
?実際には「意味」から「意図」を取らなければな らないことが多々あります。 ?undocumentedな実装
?なぜかなにをやりたいのか全然分からない回答 ?その場合でも、全体の成す意図は分かっているこ とが多いので、フローを元に全体の意図を分割し ましょう。 ?ちなみに、意味が分からない(ものの解釈をしようが ない)のならどうしようもありません。
28.
あなたの説明は 意図と 意味を
分けていますか?
30.
参考:記号的に表示した計算の流れ ?0:1:zipWith (+)
fibs (tail fibs)
31.
fibsを展開 (どの部分まで計算したかを覚える) ?0:1:zipWith
(+) fibs (tail fibs) = 0:1:zipWith (+) (0:1:ここから先) (tail (0:1:ここから先))
32.
zipWithを進める (tailはスキップしている) ?0:1:zipWith
(+) fibs (tail fibs) = 0:1:zipWith (+) (0:1:ここから先) (tail (0:1:ここから先)) = 0:1:1:zipWith (+) (1:ここから先) (ここから先)
33.
決定した部分で進行 ?0:1:zipWith (+)
fibs (tail fibs) = 0:1:zipWith (+) (0:1:ここから先) (tail (0:1:ここから先)) = 0:1:1:zipWith (+) (1:ここから先) (ここから先) = 0:1:1:zipWith (+) (1:1:ここから先) (1:ここから先) 次は1だとわかったから矢印を進めた
34.
zipWithを進める ?0:1:zipWith (+)
fibs (tail fibs) = 0:1:zipWith (+) (0:1:ここから先) (tail (0:1:ここから先)) = 0:1:1:zipWith (+) (1:ここから先) (ここから先) = 0:1:1:zipWith (+) (1:1:ここから先) (1:ここから先) = 0:1:1:2:zipWith (+) (1:ここから先) (ここから先)
Download