Clojure中惰性构建集合的方法

3

我开始学习Clojure,但是对于某些概念我感到困惑。例如,我想做的是将这个函数转换为调用get-origlabels的惰性执行。

(defn get-all-origlabels []
    (set (flatten (map get-origlabels (range *song-count*)))))

我的第一次尝试使用递归,但是由于歌曲数约为10,000,导致堆栈溢出。我无法想出如何使用尾递归来解决这个问题。

每次调用get-origlabels函数时都会返回一个集合,但是值经常在调用之间重复。实际上,get-origlabels函数的作用是读取一个文件(对于从0到歌曲数-1的每个值都是不同的文件),并将其中存储的单词以集合形式返回。

非常感谢任何指针!

谢谢! -Philip

3个回答

8
你可以使用 mapcat 来得到你想要的结果。我相信将其放入实际的 Clojure 集合中将使其去惰性化,这是通过以下事实演示的:(take 10 (set (iterate inc 0))) 在取出前会尝试实现整个集合。
(distinct (mapcat get-origlabels (range *song-count*)))

这将为您提供一个惰性序列。您可以通过执行以下操作进行验证,例如从一个无限序列开始:
(->> (iterate inc 0)
     (mapcat vector)
     (distinct)
     (take 10))

由于你似乎真的想要实现延迟计算,因此最终得到的是一个序列而不是集合,但我认为这样做是最好的。


+1 表示解释了 set 函数“去惰性化”序列的过程。现在我们迫切需要一个动词来描述这个过程。 - Leonel
1
"实现"这个单词是表示此意的标准动词;我好像已经懒得回忆它了 :) - trptcolin
2
(iterate inc 0) 和 (range) 是一样的,所以你可以缩短它一点。 - nickik
2
你也可以说懒惰的反义词是“渴望”。set函数是渴望执行的。 - Stuart Sierra

1

这可能具有更好的堆栈行为

(defn get-all-origlabels []
    (reduce (fn (s x) (union s (get-origlabels x))) ${} (range *song-count*)))

1

我可能会使用类似这样的东西:

(into #{} (mapcat get-origlabels (range *song-count*)))

通常情况下,在构建Clojure数据结构时,“into”非常有帮助。我脑海中有这样一个形象,就像是一个传送带(一个序列)将一堆随机的物体投放到一个大桶(目标集合)中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接