2012-02-23 14 views
3

印刷された戻り値は次のようになりますように、私は(すべてのベクトルに包まれた)シーケンス、番号と私の関数からハッシュマップを返す必要があります:私の入力が大きくなる可能性があるのでclojureハッシュマップの怠惰は理にかなっていますか?

[ ([:c :a] [:e :c] [:f :e] [:d :e] [:g :f] [:b :a]) 15 
    {:g :c, :f :a, :c :e, :d :a, :b :a, :c :a} ] 

、私は、関数からレイジーシーケンス/オブジェクトを返すようにしたいと思います。 ペアのシーケンス(戻りベクトルの最初のオブジェクト)は、それを構築するconj呼び出しの周りに 'lazy-seq'をラップすることによって怠惰にするのは簡単でした。

ハッシュマップ(戻りベクトルの3番目のオブジェクトで、おそらくは自分のシーケンスのように非常に大きい)は、シーケンスと同じループ - 再帰ブロック内に組み立てられています(assoc呼び出しを使用)。ハッシュマップは、私の呼び出し側の一部が使用する追加情報ですが、ペアシーケンスが怠け者として返された場合、潜在的に巨大なハッシュマップを(効率的な)lazy-seqで返すのが理にかなっているのだろうかと思います私はそれをオプションの戻り値にしても。ハッシュマップ内のエントリは、レイジーシーケンス内のペアに関連しています。

これは私のnoobie質問です:大規模なHashMapの代わりにMapEntry'sの遅延シーケンスを返信する意味はありますか?つまり、ユーザーがMapEntrysの遅延セグを取得すると仮定した場合、それらをハッシュマップに変換してルックアップを実行し、次のチャンクなどを取ることになります。これは連想データを遅延して使用する賢明な方法ですか? Clojureで大きな関連データを返す/管理するための慣用的な方法はありますか? 私の選択肢が何であるかについては、何か考えていただければ幸いです。あなたの助けを前にありがとう。彼らに怠惰なマップを与え、ノー

答えて

6

({} S内)だけで使用して、配列からマップを構築することができ、すべての呼び出し側でマップを返す理由を与えた例から

+0

あなたのアイデアをお寄せいただきありがとうございます。キーと値はどちらも安価です(両方ともキーワードです)。ちょうどそれらの多数があるかもしれない。返されるハッシュマップには、本質的に一方から他方へのポインタが含まれます。私はあなたの考え方に興味を持っています。 loop-recurブロックで累進的に構築されているMapの遅延をどうやって構築できるか詳しく教えてください。 あなたはこれを意味しますか: '(loop [my-map {} my-pairs [] ...] ... (recur(遅延assoc my-map kv))))' – Don

+0

あなた(または少なくとも私はあなたがすべきことを示唆していなかった)遅れて地図を徐々に構築することはできません。地図全体の建物を遅らせるだけです。強制すると地図全体が表示されます。 '(delay(loop [my-map {})(if(...)(recur ...)my-map)))'となります。 – amalloy

+0

OK。同じループ再帰ブロックで私の怠惰なseqをビルドすると、少し難しくなります。しかし、あなたは私に何かを与えてくれたことは間違いありません。 – Don

0

することはできません。 MapEntriesの遅延配列は可能ですが、あまり有用ではありません。しかし、似ている意味があるかもしれない他の多くのオプションがあります。

  • あなたは、呼び出し元がまったくマップを必要としないかもしれませんと言う:そう、彼らはそれを必要とする場合、彼らは強制することができ、マップの遅延を返します。
  • キーを計算するのが安いが、値が高価な場合は、正しいキーとそれぞれの値を持つ完全なマップを返すことができます。呼び出し元は必要な値のみを強制的に適用できます。

あなたはまだ(私は彼らMapEntriesなって気にしないだろう)ベクターのレイジー配列を返すことができますが、呼び出し側は怠惰なマップとしてこれを扱うことができるようになります方法は基本的にありません。どちらかといえば、すでに知られている固定キーセットを検索したいだけです(エントリを怠ってフィルタリングしたり、マップを作成したりすることはありません)、エントリを任意に検索したい場合は、最初のエントリを参照した後ですべてのエントリをメモリに保持しなければならないので、2番目のエントリを参照できるので、完全に実現されたマップにすべてをダンプすることもできます。

+0

申し訳ありませんが、わかりませんでした。マップを定義するMapEntryペアがシーケンス内のペアと異なる可能性があるため、呼び出し元がマップを構築することはできません。上記の私の例では、彼らはちょうど同様のやり方で関係しています。つまり、dと:aはシーケンス内のペアではないMapEntryに一緒になることができます。また、ペア内の2つの値の順序が異なる場合もあります。私はそれを明確にするために私の例を修正します。 – Don

+0

発信者がマップに組み込むことができる2つの遅延シーケンスを持つことができます – Retief

1

いいえ、Clojureには遅延マップがありません。

また、ループ/再帰を使ってシーケンスを構築する場合、私はそれを怠惰にしようとすると(各要素の生成が遅い場合を除いて)何かを達成するとは思わない。この2つの機能で

ルック:

(defn bad-lazy-range [begin end] 
    (loop [i (dec end) lst nil] 
    (if (>= i begin) 
     (recur (dec i) (lazy-seq (cons i lst))) 
     lst))) 

(defn good-lazy-range [begin end] 
    (if (>= begin end) 
    nil 
    (lazy-seq (cons begin (good-lazy-range (inc begin) end))))) 

bad-lazy-rangeはサンク(怠惰なシーケンスリンク)ごとに生成する、begin-end回再発して、最も外側のサンクを返します。このサンクは、次のサンクへの参照を保持する必要があります。サンクは3番目のサンクなどへの参照を必要とします。すべての作業を即座に行い、通常のリストよりも多くのスペースを占める擬似リンクリストを生成します。

good-lazy-rangeしかし、再帰呼び出しはサンクの内部に隠され、必要になるまで評価されません。これにより、スタックオーバフロー例外も防止されます。lazy-seqコールがなければ、スタックオーバーフロー例外が生成される可能性がありますが、各ステップでgood-lazy-rangeの呼び出しを評価して戻ります。呼び出し元は、次の呼び出しを評価できますが、この時点では、最初の呼び出しからのスタックフレームは長くなくなっています。

大量の計算でラップできるのであれば、通常はlazy-seqを使用してください。最初の関数では、consへの呼び出しをラップするだけです。とにかくすぐに返されます。しかし、2番目の関数では、consと再帰呼び出しの呼び出しをラップしています。これは、相当な量の計算を遅らせることを意味します。

コードでレイジーを正しく使用し、ループ/再帰を使用する場合は、投稿してください - あなたがどのようにしたのか興味があります。

+0

あなたの素晴らしい洞察と実例をお寄せいただきありがとうございます。それは理にかなっている。あなたの例では、consed(lst)オブジェクトはかなり小さいです。一方、重要な計算の結果consuedされたオブジェクトが得られた場合は、このようなレイジー・セグ・コールを持つことは意味があるように思えます(申し訳ありませんが、それは整形されていません): ' loop [..lst nil] .. (終了の場合cond-met? lst (recur ...(lazy-seq(cons x(reduce)(process-it %% 2)my-large-map ))))) ' あなたは同意しますか? – Don

+0

はい - この場合、重要な計算の周りに 'lazy-seq'をラッピングしているので便利です。重要なのは、 'lazy-seq'が再帰呼び出しの回りにラップされているのではなく、簡単な計算でラップされていることです。再帰呼び出しは、単純で一般的ではありません。 – Retief

関連する問題