过滤,然后映射?还是只使用 for 循环?

Filter, then map? Or just use a for loop?

我一直运行遇到需要通过某种函数过滤一组地图的情况,然后从每个生成的地图中提取一个值以构成我的最终集合。

我经常使用这个基本结构:

(map :key (filter some-predicate coll))

我突然想到,这基本上完成了与 for 循环相同的事情:

(for [x coll :when (some-predicate x)] (:key x))

一种方法比另一种方法更有效吗?我认为 for 版本会更有效率,因为我们只浏览一次集合。这准确吗?

两者都没有显着差异。

这两个 return 一个未实现的惰性序列,每次读取一个项目时都会对其进行计算。第一个不会遍历列表两次,而是创建一个惰性序列,该序列产生与过滤器匹配的项目,然后立即被 map 函数消费(仍然是惰性的)。因此,在第一种情况下,您有一个惰性序列懒惰地消耗另一个惰性序列中的项目。另一方面,对 for 的调用会产生一个惰性序列,每一步都有很多逻辑。

您可以看到示例扩展成的代码:

(pprint (macroexpand-1 '(for [x coll :when (some-predicate x)] (:key x)))) 

总体而言,性能与第二种方法非常相似,可能产生的垃圾略少,因此您根据性能在这两种方法之间做出决定的唯一方法是进行基准测试。根据风格,我选择第一个,因为它更短,但如果有更多的阶段,我可能会选择用 thread-last 宏来编写它。

(->> coll
     (filter some-predicate)
     (take some-limit)
     (map :key))

虽然这基本上归结为个人风格