在Clojure中通过"窗口"谓词对seq进行分区

Sco*_*ach 9 clojure

我希望将seq"chunk"为subseqs,与partition-by相同,不同的是该函数不应用于每个单独的元素,而是应用于一系列元素.

所以,例如:

(gather (fn [a b] (> (- b a) 2)) 
        [1 4 5 8 9 10 15 20 21])
Run Code Online (Sandbox Code Playgroud)

会导致:

[[1] [4 5] [8 9 10] [15] [20 21]]
Run Code Online (Sandbox Code Playgroud)

同样:

(defn f [a b] (> (- b a) 2))
(gather f [1 2 3 4]) ;; => [[1 2 3] [4]]
(gather f [1 2 3 4 5 6 7 8 9]) ;; => [[1 2 3] [4 5 6] [7 8 9]]
Run Code Online (Sandbox Code Playgroud)

我的想法是我将列表的开头和下一个元素应用于函数,如果函数返回true,我们将列表的当前头部分区分割到新的分区.

我写过:

(defn gather
  [pred? lst]
  (loop [acc [] cur [] l lst]
    (let [a (first cur)
          b (first l)
          nxt (conj cur b)
          rst (rest l)]
      (cond
       (empty? l) (conj acc cur)
       (empty? cur) (recur acc nxt rst)
       ((complement pred?) a b) (recur acc nxt rst)
       :else (recur (conj acc cur) [b] rst)))))
Run Code Online (Sandbox Code Playgroud)

它有效,但我知道有一种更简单的方法.我的问题是:

是否有内置函数来执行此操作,此功能是不必要的?如果没有,是否有一个我忽略的更惯用(或更简单)的解决方案?结合减少和吃东西的东西?

谢谢.

A. *_*ebb 11

问题的原始解释

我们(所有)似乎误解了你的问题,因为只要谓词持续连续元素,就想要开始一个新的分区.

另一个,懒惰,建立在 partition-by

(defn partition-between [pred? coll] 
  (let [switch (reductions not= true (map pred? coll (rest coll)))] 
    (map (partial map first) (partition-by second (map list coll switch)))))
Run Code Online (Sandbox Code Playgroud)
(partition-between (fn [a b] (> (- b a) 2)) [1 4 5 8 9 10 15 20 21])
;=> ((1) (4 5) (8 9 10) (15) (20 21))
Run Code Online (Sandbox Code Playgroud)

实际问题

实际问题要求我们在pred?保持当前分区和当前元素的开头时启动新分区.为此,我们可以partition-by通过一些调整来解决它的来源.

(defn gather [pred? coll]
  (lazy-seq
   (when-let [s (seq coll)]
     (let [fst (first s)
           run (cons fst (take-while #((complement pred?) fst %) (next s)))]
       (cons run (gather pred? (seq (drop (count run) s))))))))
Run Code Online (Sandbox Code Playgroud)
(gather (fn [a b] (> (- b a) 2)) [1 4 5 8 9 10 15 20 21])
;=> ((1) (4 5) (8 9 10) (15) (20 21))

(gather (fn [a b] (> (- b a) 2)) [1 2 3 4])
;=> ((1 2 3) (4))

(gather (fn [a b] (> (- b a) 2)) [1 2 3 4 5 6 7 8 9])
;=> ((1 2 3) (4 5 6) (7 8 9))
Run Code Online (Sandbox Code Playgroud)