clojure哈希映射的惰性有意义吗?

clojure哈希映射的惰性有意义吗?,clojure,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”而变得懒惰 散列映射(我的返

我需要从我的函数中返回一个序列、一个数字和一个哈希映射(都包装在一个向量中),以便打印的返回值如下所示:

[ ([: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调用)。hash映射是我的一些调用者将使用的附加信息,但是如果pairs序列返回为lazy,那么我想知道,即使我将其作为可选的返回值,使用(有效的)lazy seq返回一个潜在的巨大hash映射是否有意义。散列映射中的条目与延迟序列中的对相关

所以我的问题是:用一个懒散的MapEntry序列来代替一个大的HashMap有什么意义?也就是说,假设用户将获取MapEntrys的惰性seq的一个块,将它们转换为hashmap进行查找。如果查找失败,他们将获取下一个块,依此类推。这是一种懒散地使用关联数据的明智方法吗? 在Clojure中是否有一些惯用的方法来返回/管理大型关联数据?
如果您对我的选择有任何想法,我将不胜感激。提前感谢您的帮助。

在您给出的示例中,调用方可以使用(到{}s)从seq构建映射。

不,给他们一个惰性映射是不可能的。映射项的惰性序列是可能的,但不是很有用。不过,还有许多其他类似的选择可能是有意义的

  • 您说调用方可能根本不需要映射:因此返回映射的延迟,如果需要,可以强制执行
  • 如果键的计算成本很低,但值的计算成本很高,则可以返回一个完整的映射,其中包含正确的键和每个延迟的值;调用者只能强制他们需要的值

您仍然可以返回一个懒散的向量序列(我不想麻烦将它们设置为MapEntries),但是调用者基本上不可能将其视为懒散的映射。要么他们只想查找一组固定的已知密钥(在这种情况下,他们只是懒洋洋地筛选条目,从不将其映射),要么他们想随意查找条目,在这种情况下,他们必须在查找第一个条目后将所有条目保留在内存中,以便他们仍然可以查找第二个条目,因此,他们不妨将整个内容转储到一个完全实现的映射中。

不,Clojure没有惰性映射

另外,如果您正在使用循环/递归构建序列,我不相信尝试使其变为惰性会实现任何事情(除非生成每个元素的速度很慢)

看看这两个功能:

(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)))))
不良延迟范围
将重复出现
开始-结束
次,每次生成一个thunk(延迟序列链接),然后返回最外层的thunk。这个thunk需要保留对下一个thunk的引用,而下一个thunk需要对第三个thunk的引用,等等。您立即完成所有工作并生成一个伪链接thunk列表,该列表比普通列表占用更多空间

然而,
良好的延迟范围
会立即返回,不再递归——递归调用隐藏在thunk中,直到必要时才会进行计算。这还可以防止堆栈溢出异常——如果没有
lazy seq
调用,它可能会生成堆栈溢出异常,但在每个步骤中,它都会计算一个对
良好延迟范围的调用并返回。然后调用方可以计算下一个调用,但此时,来自第一个调用的堆栈帧早已消失

一般来说,如果您可以将其包含在大量计算中,则只能使用
lazy seq
。在第一个函数中,它只包含对
cons
的调用,该调用无论如何都会很快返回。然而,在第二个函数中,它被包装在对
cons
的调用和递归调用中,这意味着它延迟了大量有价值的计算


如果您的代码正确使用lazyness并使用循环/重复,请发布它——我很想看看您是如何做到的。

对不起,我不清楚。调用方无法生成映射,因为定义映射的映射条目对可能与序列中的映射条目对不同。在我上面的例子中,它们恰好以类似的方式联系在一起。我的意思是:d和:a可以在一个MapEntry中同时出现,而不是序列中的一对。此外,该对中2个值的顺序可能不同。我将修改我的示例来澄清这一点。您可以有两个惰性序列,它们可以由调用方银行构建到映射中。键和值都同样便宜(都是关键字);可能有很多。要返回的哈希映射基本上包含从一个到另一个的指针。我对你延期的想法很感兴趣。能否请您详细说明如何为循环重现块中逐步建立的映射建立延迟?你的意思是:
(循环[my map{}my pairs[]…](重复(延迟(assoc my map k v)))
你不能(或者至少我没有建议你应该)在延迟中逐步构建映射。只要延迟整个地图的构建:如果他们强制,他们就可以得到整个地图<代码>(延迟(循环[my map{}](如果(…)(重复…)my map))
。确定。当我在同一个循环递归块中构建我的lazy seq时,这将有点难以实现。但你确实给了我一些东西来帮助我。谢谢你的真知灼见和说明性的例子。这是有道理的。在您的示例中,使用的对象(lst)相当小。在t上