これは、あなたが学習するつもりだったプログラミング言語をより流に習得するための良い演習ですが、ほんの少しだけいじっています。これには、オブジェクトの操作、クロージャーの使用またはシミュレーション、型システムの拡張が含まれます。
あなたのタスクは、遅延リストを管理するコードを記述し、それを使用してフィボナッチ数を生成するためのこのアルゴリズムを実装することです。
コードサンプルはHaskellにあります
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
結果:
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]
遅延リストの実装は、次のガイドラインを満たす必要があります。
- リストノードは、次の3つのいずれかです。
- Nil-空のリスト。
[]
- 短所-単一のアイテム、残りの項目のリストとペア:
1 : [2,3,4,5]
(:
Haskellの短所演算子です) - サンク-必要なときにListノードを生成する遅延計算。
- Nil-空のリスト。
- 次の操作をサポートしています。
- nil-空のリストを作成します。
- cons-コンスセルを構築します。
- thunk-引数を取らず、NilまたはConsを返す関数を指定して、Thunkを構築します。
- force-リストノードが与えられた場合:
- NilまたはConsの場合は、単にそれを返します。
- サンクの場合、その関数を呼び出してNilまたはConsを取得します。サンクをそのNilまたはConsに置き換えて返します。
注:サンクを強制値に置き換えることは、「遅延」の定義の重要な部分です。このステップをスキップすると、上記のフィボナッチアルゴリズムは非常に遅くなります。
- empty-ListノードがNilかどうかを確認します(強制した後)。
- head(別名「車」)-リストの最初の項目を取得します(またはNilの場合は適合をスローします)。
- tail(別名「cdr」)-リストの先頭の後の要素を取得します(または、Nilの場合は適合をスローします)。
- zipWith-バイナリ関数(例
(+)
)と2つの(おそらく無限の)リストが与えられた場合、リストの対応するアイテムに関数を適用します。例:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take-数Nと(場合によっては無限の)リストを指定して、リストの最初のN個のアイテムを取得します。
- print-リスト内のすべてのアイテムを印刷します。これは、長いリストまたは無限のリストが指定された場合に徐々に機能するはずです。
fibs
独自の定義で自身を使用します。遅延再帰を設定するのは少し厄介です。次のようなことをする必要があります。- にサンクを割り当て
fibs
ます。今のところ、ダミーの状態のままにしておきます。 - への参照に依存するサンク関数を定義します
fibs
ます。 - サンクをその機能で更新します。
fix
独自の戻り値を使用してリストを返す関数を呼び出す関数を定義することにより、この配管を非表示にすることができます。このアイデアが着手できるように、短い昼寝を検討してください。- にサンクを割り当て
ポリモーフィズム(任意のタイプのアイテムのリストを操作する機能)は必要ありませんが、言語で慣用的な方法を見つけることができるかどうかを確認してください。
- メモリ管理について心配する必要はありません。ガベージコレクションを使用する言語でさえ、使用しない(呼び出しスタックなど)ことのないオブジェクトを持ち歩く傾向があるため、無限リストを走査している間にプログラムがメモリをリークしても驚かないでください。
これらのガイドラインからわずかに逸脱して、言語の詳細に対応したり、遅延リストへの代替アプローチを検討してください。
ルール:
- よく知らない言語を選んでください。これを「要求」することはできません。したがって、「honor-system」タグです。ただし、投票者は履歴をチェックして、投稿した言語を確認できます。
言語の組み込みの遅延リストサポートを使用してすべてを実行しないでください。実質的または少なくとも興味深いものを投稿してください。
Haskellはほとんど出ています。つまり、次のようなことをしない限り:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
注:Haskellの非厳密な評価は立ち入り禁止ではありませんが、レイジーリストの実装はそこから直接その機能を引き出すべきではありません。実際、怠lazを必要としない効率的で純粋に機能的なソリューションを見るのは興味深いでしょう。
Python:
- itertoolsを使用しないでください。
- ジェネレータは問題ありませんが、ジェネレータを使用する場合、強制された値をメモする方法を見つける必要があります。
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
。ただし、上記のフィボナッチアルゴリズムでは、zipWithの両方の引数が無限リストであるため、これは重要ではありません。
zipWith
長さの異なる2つのリストを呼び出すときの動作はどうなりますか?