英文:
In clojure how to partition an array of sorted integers into contiguous partitions?
问题
以下是翻译好的部分:
"I'd like to partition an array of sorted integers into contiguous partitions."
"我想将一个已排序的整数数组分成连续的分区。"
"The following ruby:"
"下面的 Ruby 代码:"
"Outputs:"
"输出:"
"How can I do this in clojure?"
"我怎样在 Clojure 中实现这个?"
"I tried using partition-by
, but AFAIK it only takes one argument."
"我尝试使用 partition-by
,但据我所知它只接受一个参数。"
英文:
I'd like to partition an array of sorted integers into contiguous partitions.
The following ruby:
[1,2,3,8,9,10,99].slice_when { |x, y| y > x + 1 }.to_a
Outputs:
[[1, 2, 3], [8, 9, 10], [99]]
How can I do this in clojure?
I tried using partition-by
, but AFAIK it only takes one argument.
答案1
得分: 4
你必须在某个地方保存上一个元素。在这种情况下,你可以这样做:
(defn slice [coll]
(let [prev (atom (first coll))]
(->> coll
(partition-by #(> (inc @prev) (reset! prev %))))))
(slice [1 2 3 8 9 10 99])
=> ((1 2 3) (8 9 10) (99))
如果你还想提供这个函数,你将需要编写一个基于reduce
的解决方案:
(defn slice-when [pred coll]
(->> coll
(reduce (fn [acc current]
(if-let [previous (peek (peek acc))]
(if (pred previous current)
(conj acc [current])
(conj (pop acc) (conj (peek acc) current)))
(conj acc [current])))
[])))
示例:
(slice-when (fn [x y] (> y (inc x)))
[1 2 3 8 9 10 99])
=> [[1 2 3] [8 9 10] [99]]
英文:
You have to save somewhere that previous element. In this case, you can do:
(defn slice [coll]
(let [prev (atom (first coll))]
(->> coll
(partition-by #(< (inc @prev) (reset! prev %))))))
(slice [1 2 3 8 9 10 99])
=> ((1 2 3) (8 9 10) (99))
If you also want to provide that function, you will have to write some reduce
-based solution:
(defn slice-when [pred coll]
(->> coll
(reduce (fn [acc current]
(if-let [previous (peek (peek acc))]
(if (pred previous current)
(conj acc [current])
(conj (pop acc) (conj (peek acc) current)))
(conj acc [current])))
[])))
Example:
(slice-when (fn [x y] (> y (inc x)))
[1 2 3 8 9 10 99])
=> [[1 2 3] [8 9 10] [99]]
答案2
得分: 1
你也可以标记所有的分割点,然后只是根据它们进行分区。
(defn split-when [pred data]
(when-let [[x & xs] (seq data)]
(->> (mapcat (fn [a b] (if (pred a b) [::split b] [b]))
data xs)
(cons x)
(partition-by #{::split})
(take-nth 2))))
用户> (split-when (complement (comp #{-1} -)) [1 5 6 7 9 10 22])
;;=> ((1) (5 6 7) (9 10) (22))
或者,你可以收集应该分割的所有索引,并传递给subvec
:
(defn split-when [pred data]
(when (seq data)
(let [splits (keep-indexed (fn [i [a b]] (when (pred a b) (inc i)))
(partition 2 1 data))
indices `[0 ~@splits ~(count data)]]
(map (partial subvec (vec data)) indices (rest indices)))))
用户> (split-when (complement (comp #{-1} -)) [1 5 6 7 9 10 22])
;;=> (1 [5 6 7] [9 10] [22])
英文:
you could also mark all the split points, and then just partition with them
(defn split-when [pred data]
(when-let [[x & xs] (seq data)]
(->> (mapcat (fn [a b] (if (pred a b) [::split b] [b]))
data xs)
(cons x)
(partition-by #{::split})
(take-nth 2))))
user> (split-when (complement (comp #{-1} -)) [1 5 6 7 9 10 22])
;;=> ((1) (5 6 7) (9 10) (22))
otherwise you can collect all the indices where you should split and pass over to subvec
:
(defn split-when [pred data]
(when (seq data)
(let [splits (keep-indexed (fn [i [a b]] (when (pred a b) (inc i)))
(partition 2 1 data))
indices `[0 ~@splits ~(count data)]]
(map (partial subvec (vec data)) indices (rest indices)))))
user> (split-when (complement (comp #{-1} -)) [1 5 6 7 9 10 22])
;;=> ([1] [5 6 7] [9 10] [22])
答案3
得分: 1
以下是您提供的内容的中文翻译部分:
我编写了一个通用版本,如下所示。
它将数据向量分割为所有可能的点,将子向量传递给类似于以下的谓词函数:
(fn [data1 data2] ...)
它跟踪所有谓词返回 true 的索引,然后使用 reduce
和 subvec
来累积结果。我包含了一个单元测试。
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
; 我们使用 `split-at` 来对集合进行分区。对于像 [0 1 2] 这样的集合,
; 提供像 {:data1 [] :data2 [0 1 2]} 或 {:data1 [0 1 2] :data2 []} 这样的退化分割是没有意义的。
; 因此,我们需要在范围 [1..(N-1)] 内找到分割点
(verify
(let [data [0 1 2]
N (count data)
result (forv [i (thru N)]
(let [[data1 data2] (split-at i data)]
(vals->map i data1 data2)))]
(is= result
[{:i 0 :data1 [] :data2 [0 1 2]}
{:i 1 :data1 [0] :data2 [1 2]}
{:i 2 :data1 [0 1] :data2 [2]}
{:i 3 :data1 [0 1 2] :data2 []}])))
因此,我们避免了 data1 或 data2 中的一个为空向量的退化情况。
(defn partition-when
[pred coll]
(let [data (vec coll)
N (count data)
N-1 (dec N)
; 我们使用 `split-at` 来对集合进行分区。对于像 [0 1 2] 这样的集合,
; 提供像 {:data1 [] :data2 [0 1 2]} 或 {:data1 [0 1 2] :data2 []} 这样的退化分割是没有意义的。
; 因此,我们需要在范围 [1..(N-1)] 内找到分割点
split-idxs (loop [i 1
idxs []]
(if-not (< i N-1)
idxs
(let [[data1 data2] (split-at i data)
split? (pred data1 data2)
result-next (if split?
(append idxs i)
idxs)
i-next (inc i)]
(recur i-next result-next))))
; 由于我们使用 `subvec`,我们必须在向量的开始/结束添加“默认”值
split-idxs (glue [0] split-idxs [N])
idx-pairs (partition 2 1 split-idxs)
subvecs (reduce (fn [cum [start end]]
(let [next-part (subvec data start end)
next-cum (append cum next-part)]
next-cum))
[]
idx-pairs)]
subvecs))
以及单元测试。请注意,谓词函数必须负责从 data1
和 data2
中获取单个元素。
(verify
(let [split-fn (fn [data1 data2]
(let [x (last data1)
y (first data2)]
(< (inc x) y)))]
(is= [[1 2 3] [8 9 10] [99]]
(partition-when split-fn
[1 2 3 8 9 10 99]))))
这个函数现在是 Tupelo 库的一部分。
英文:
I wrote a general version shown below.
It splits the data vector at all possible points, presenting the sub-vectors to a predicate function that looks like:
(fn [data1 data2] ...)
It keeps track of all the indexes where the pred returns true, then uses reduce
and subvec
to accumulate the result. I've included a unit test.
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
; We use `split-at` to partition the coll. Given a coll like [0 1 2], it makes
; no sense to give degenerate splits like {:data1 [] :data2 [0 1 2]}
; or {:data1 [0 1 2] :data2 []}. So, we need the split point in the range [1..(N-1)]
(verify
(let [data [0 1 2]
N (count data)
result (forv [i (thru N)]
(let [[data1 data2] (split-at i data)]
(vals->map i data1 data2)))]
(is= result
[{:i 0 :data1 [] :data2 [0 1 2]}
{:i 1 :data1 [0] :data2 [1 2]}
{:i 2 :data1 [0 1] :data2 [2]}
{:i 3 :data1 [0 1 2] :data2 []}])))
So, we avoid the degenerate case where either data1 or data2 is the empty vector.
(defn partition-when
[pred coll]
(let [data (vec coll)
N (count data)
N-1 (dec N)
; We use `split-at` to partition the coll. Given a coll like [0 1 2], it makes
; no sense to give degenerate splits like {:data1 [] :data2 [0 1 2]}
; or {:data1 [0 1 2] :data2 []}. So, we need the split point in the range [1..(N-1)]
split-idxs (loop [i 1
idxs []]
(if-not (<= i N-1)
idxs
(let [[data1 data2] (split-at i data)
split? (pred data1 data2)
result-next (if split?
(append idxs i)
idxs)
i-next (inc i)]
(recur i-next result-next))))
; Since we are using `subvec`, we must add in the "default" values at the
; start/end of the vector
split-idxs (glue [0] split-idxs [N])
idx-pairs (partition 2 1 split-idxs)
subvecs (reduce (fn [cum [start end]]
(let [next-part (subvec data start end)
next-cum (append cum next-part)]
next-cum))
[]
idx-pairs)]
subvecs))
and the unit test. Notice the predicate function must take responsibility for pulling individual elements out of data1
and data2
(verify
(let [split-fn (fn [data1 data2]
(let [x (last data1)
y (first data2)]
(< (inc x) y)))]
(is= [[1 2 3] [8 9 10] [99]]
(partition-when split-fn
[1 2 3 8 9 10 99])))
)
This function is now part of the Tupelo library.
答案4
得分: 0
我觉得这个解决方案非常简洁:
(defn partition-contiguous [numbers]
(->> numbers
(partition-by identity)
(map-indexed cons)
(partition-by (fn [[x y]] (- x y)))
(map #(mapcat rest %))))
(partition-contiguous [1 2 3 3 8 9 9 10 10 99])
;; => ((1 2 3 3) (8 9 9 10 10) (99))
英文:
I find this solution pretty concise:
(defn partition-contiguous [numbers]
(->> numbers
(partition-by identity)
(map-indexed cons)
(partition-by (fn [[x y]] (- x y)))
(map #(mapcat rest %))))
(partition-contiguous [1 2 3 3 8 9 9 10 10 99])
;; => ((1 2 3 3) (8 9 9 10 10) (99))
答案5
得分: 0
The library dev.weavejester/medley ("一个小而有用的函数集合,大多数函数在 clojure.core 命名空间中都不会显得不合适") 自 1.7.0 版本以来包含了 partition-between
函数。该函数可用于将已排序的集合划分为连续的分区:
(require '[medley.core :refer [partition-between]])
(partition-between (fn [x y] (> y (inc x))) [1 2 3 8 9 10 99])
;; => ((1 2 3) (8 9 10) (99))
英文:
The library dev.weavejester/medley ("A small collection of useful, mostly pure functions that might not look out of place in the clojure.core namespace") contains partition-between since 1.7.0. That function can be used to partition a sorted collection into contiguous partitions:
(require '[medley.core :refer [partition-between]])
(partition-between (fn [x y] (> y (inc x))) [1 2 3 8 9 10 99])
;; => ((1 2 3) (8 9 10) (99))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论