clojure哈希映射的懒惰是否有意义?

时间:2012-02-23 20:07:18

标签: 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'来使其变得懒惰。

哈希映射(我的返回向量中的第三个对象,可能非常大,就像我的序列一样)在与序列相同的循环 - 重复块中构建(使用assoc调用)。哈希映射是我的一些调用者将使用的附加信息,但如果对序列被返回为惰性,那么我想知道是否有意义发回一个潜在的巨大哈希映射与(一个有效的)lazy-seq即使我把它作为一个可选的返回值。散列映射中的条目与惰性序列中的对相关。

所以这是我的noobie问题:在发送一个懒惰的MapEntry序列代替大型HashMap时有什么意义吗?也就是说,假设用户将获取一小部分laEq-Map的MapEntrys,将它们转换为hashmap以进行查找...即可获取下一个块,依此类推。这是一种懒惰地使用关联数据的合理方法吗? 在Clojure中是否有一些惯用的方法来返回/管理大型关联数据? 我很欣赏任何关于我的选择的想法。在此先感谢您的帮助。

3 个答案:

答案 0 :(得分:6)

不,给他们一张懒惰的地图是不可能的。一个懒惰的MapEntries序列是可能的,但不是很有用。但是,有许多其他可能有意义的选项是相似的。

  • 您说调用者可能根本不需要地图:因此请返回地图的延迟,如果他们需要,可以强制执行。
  • 如果键计算成本低,但价值昂贵,您可以返回带有正确键的完整映射,以及每个延迟的值;调用者只能强制他们需要的值。

你仍然可以返回一个lazy-seq的向量(我不打算让它们成为MapEntries),但是调用者基本上没办法把它当作一个懒惰的地图。要么他们只想查找一组固定的已知密钥(在这种情况下,他们只是懒得过滤条目,从不使它成为地图)或者他们想要任意查找条目,在这种情况下他们会在查找第一个条目后,必须将所有条目保留在内存中,这样它们仍然可以查找第二个条目,因此它们也可以将整个条目转储到完全实现的映射中。

答案 1 :(得分:1)

不,Clojure没有懒人地图。

另外,如果你使用loop / recur构建一个序列,我不相信试图让它变得懒惰可以完成任何事情(除非生成每个元素很慢)。

看看这两个功能:

(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次,每次生成一个thunk(一个懒惰的序列链接),然后返回最外面的thunk。这个thunk需要保持对下一个thunk的引用,这需要引用第三个thunk等。你立即完成所有工作并生成一个伪链接的thunks列表,占用的空间比普通列表要多。< / p> 然而,

good-lazy-range会立即返回而不会再递增更多 - 递归调用隐藏在thunk中,并且在必要时不会被评估。这也可以防止堆栈溢出异常 - 如果没有lazy-seq调用,它可能会生成堆栈溢出异常,但在每一步,它都会计算对good-lazy-range的一次调用并返回。然后调用者可以评估下一个调用,但此时,第一次调用的堆栈帧早已消失。

通常,只有使用lazy-seq才能将其包含在大量计算中。在第一个函数中,它仅包含在cons的调用中,无论如何都会快速返回。然而,在第二个函数中,它围绕着对cons的调用和递归调用,这意味着它会延迟一个有价值的计算量。

如果您的代码正确使用了lazyness并使用了loop / recur,请发布它 - 我很想知道您是如何做到的。

答案 2 :(得分:0)

从你举例说明为什么在所有调用者处返回地图可以使用(进入{} s)从seq构建地图