Clojure - 让first和filter变成惰性求值

4
我正在学习Clojure。在解决一个问题时,我必须使用first+filter。我注意到过滤器对于所有输入都是不必要的。如何使filter惰性运行,以便它不需要为整个输入应用谓词。
以下是一个示例,说明它不是惰性的:
(defn filter-even
  [n]
  (println n)
  (= (mod n 2) 0))

(first (filter filter-even (range 1 4)))

上面的代码会打印出:

1

2

3

但实际上只需打印到2,如何才能使其变成惰性呢?

1
你可以考虑使用 some 而不是 (first (filter ...)) - exupero
1个回答

10

这是因为range是一个分块序列:

(chunked-seq? (range 1))
=> true

如果可用,它将实际获取前32个元素:

(first (filter filter-even (range 1 100)))
1
2
. . .
30
31
32
=> 2

这篇概述展示了一个unchunk函数,可以防止出现这种情况。不幸的是,它并不是标准函数:

(defn unchunk [s]
  (when (seq s)
    (lazy-seq
      (cons (first s)
            (unchunk (next s))))))

(first (filter filter-even (unchunk (range 1 100))))
2
=> 2

或者,您可以将list 应用于它,因为列表不是分块的:

(first (filter filter-even (apply list (range 1 100))))
2
=> 2

但是显然,整个集合需要进行预过滤才能实现。

虽然如此,我从来没有对此过分担心过。过滤函数通常不会太昂贵,在大局观中,32个元素的块也不算太大。


1
另请参阅:https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects - Michiel Borkent
1
是的,我只是将它作为这个主题的额外信息添加了进来。 - Michiel Borkent

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