ベクターであるClojure Lazyシーケンス
-
28-09-2019 - |
質問
Clojureの怠zyなシーケンスは、リンクされたリストとして内部的に表されているように見えることに気付きました(または、少なくともそれらは要素への連続的なアクセスのみを持つシーケンスとして扱われています)。メモリにキャッシュされた後でも、怠zyなセックを介してアクセス時間 nth
o(n)であり、ベクトルのように一定の時間ではありません。
;; ...created my-lazy-seq here and used the first 50,000 items
(time (nth my-lazy-seq 10000))
"Elapsed time: 1.081325 msecs"
(time (nth my-lazy-seq 20000))
"Elapsed time: 2.554563 msecs"
Clojureで一定の時間検索を取得したり、怠zyなベクトルを作成したりするにはどうすればよいですか?
怠zyなベクターの生成中、各要素はそれより前のすべての要素の関数であるため、リストを横断するのに費やした時間が重要な要因になると想像してください。
関連する質問は、この不完全なJavaスニペットを見つけただけです。怠zyなベクターの設計:constの問題
解決
はい、Clojureのシーケンスは次のように説明されています 「論理リスト」 3つの操作(最初、次、および短所)。
シーケンスは、本質的にはイテレーターのClojureバージョンです(Clojure.orgは、シーケンスはIternal状態を保持しないため、シーケンスは繰り返しではないことを主張しています)。
少なくともClojureには怠zyなベクターは存在しません。
必要のない中間要素を計算せずに、さまざまなインデックスを一定の時間検索が必要な場合は、その結果をその場で計算する関数を使用できます。メモ化と組み合わせること(または、結果を自分でarg-to-resulthashでキャッシュする)あなたは、怠zyなベクターから望んでいると思うのとほぼ同じ効果を得ることができます。
これは、前のすべてのf(0)... f(n-1)を通過するよりも直接F(n)を計算できるアルゴリズムがある場合にのみ明らかに機能します。そのようなアルゴリズムがない場合、すべての要素の結果が以前のすべての要素の結果に依存している場合、いずれの場合でもシーケンスイテレータよりもうまく機能することはできません。
編集
ところで、あなたが望むのが結果がベクトルになることだけであるなら、その後クイックルックアップを取得し、初めて要素が順番に作成されることを気にしないでください。それは十分に簡単です。
ベクトルを使用したフィボナッチの実装は次のとおりです。
(defn vector-fib [v]
(let [a (v (- (count v) 2)) ; next-to-last element
b (peek v)] ; last element
(conj v (+ a b))))
(def fib (iterate vector-fib [1 1]))
(first (drop 10 fib))
=> [1 1 2 3 5 8 13 21 34 55 89 144]
ここでは、怠zyなシーケンスを使用して、要求されるまで関数呼び出しを延期しています(iterate
怠zyなシーケンスを返します)が、結果は収集され、ベクトルで返されます。
ベクトルは必要に応じて成長し、要素を最後の要素まで追加する要素のみを追加し、計算すると一定の時間検索になります。
それはあなたが念頭に置いていたこのようなことでしたか?