您将如何在Clojure中组合排序和筛选



假设我有一个字符串集合,我想返回所有长度超过4个字符的字符串,首先按最短字符串排序。

你可以用以下方法来解决这个问题:

(def strings ["this" "is" "super" "cool" "everybody" "isn't" "clojure" "great!?!?"])
(sort-by count < (filter #(> (count %) 4) strings))
;; > ("super" "isn't" "clojure" "everybody" "great!?!?")

请注意,我们使用了两次count。这在这里可能很好,但如果count不是count呢?如果我们称super-expensive-function而不是count,那我们真的宁愿不运行超过绝对必要的量呢?

因此:

  • 我们有很多东西
  • 我们想退回一批订购的东西
  • 使用计算成本高昂的函数的结果进行筛选和排序,该函数每次只能调用一次

有没有现有的功能可以做到这一点,或者我需要建立自己的功能?

最简单的解决方案是将每个项目与其昂贵的计算属性配对,然后过滤和排序,然后丢弃参数:

(->> strings
     (map (juxt identity count))
     (filter (fn [[_ c]] (> c 4)))
     (sort-by peek)
     (map first))

如果计算有问题的属性真的那么昂贵,那么分配向量的开销应该会几乎消失。

也许JIT编译器可以发现这个昂贵的中间结果在两个操作之间是可缓存的?考虑到手动缓存结果的复杂性增加,有必要尝试排除这种可能性。我会为各种解决方案运行几次性能测试,时间测量如下:

(time (dotimes [_ 1e5] ...))

您可以使用group-by进行配对,并使用列表理解进行聚合和过滤。

(for [[c sv] (sort-by first (group-by count strings)) :when (> c 4) s sv] s)

最新更新