新手问题:
如何在数字矢量中包含最大值的第一个实例并将其包括在内?
因此,从此[1 2 3 4 5 4 3 2 1]
开始,获取[1 2 3 4 5] [4 3 2 1]
。
我这样做的方式似乎过于复杂:
(def up5 [1 2 3 4 5 4 3 2 1])
(split-at (inc (.indexOf up5 (apply max up5))) up5) ; => [1 2 3 4 5] [4 3 2 1]
这看起来有点尴尬吗?例如,使用定义的向量三次。我们是否需要使用Java来获取索引?
什么是更好,更惯用或更高效的方式?
感谢。
答案 0 :(得分:2)
替代变体(仅为了好玩):
max-key
将您的集合拆分为所需的索引(元组中的第一项)
(defn split-at-max [items]
(->> items
(map vector (rest (range)))
(apply max-key second)
first
(#(split-at % items))))
user> (split-at-max [-1 20 3 4 1 3 5 101 4 2 6 4])
[(-1 20 3 4 1 3 5 101) (4 2 6 4)]
此外,您可以轻松修改它以使用任意标准来估算值。
(defn split-at-max [items & {identity-fn :by :or {identity-fn identity}}]
(->> items
(map vector (rest (range)))
(apply max-key (comp identity-fn second))
first
(#(split-at % items))))
最大身份:
user> (split-at-max [-1 20 3 4 1 3 5 101 4 2 6 4])
[(-1 20 3 4 1 3 5 101) (4 2 6 4)]
最大尺寸:
user> (split-at-max ["i" "wanna" "rock'n'roll" "all" "night"
"and" "party" "every" "day"]
:by count)
[("i" "wanna" "rock'n'roll") ("all" "night" "and" "party" "every" "day")]
或某些外部价值,例如:
user> (split-at-max [:a :b :c :d] :by {:a 0 :b 121 :c 2 :d -100})
[(:a :b) (:c :d)]
所以对我来说它似乎更具功能性(并且更多的是“clojure方式”),尽管可能不是最有效率的。
答案 1 :(得分:2)
如果先下订单并不重要,您可以使用此
(def up5 [1 2 3 4 5 4 3 2 1 0])
(def up5max (apply max up5)
(->> up5
reverse
(split-with (partial > up5max))
(map reverse))
#=> ((4 3 2 1 0) (1 2 3 4 5))
答案 2 :(得分:2)
如果表现很重要,我就这样做:
(defn vec-split-at [idx v]
(if (empty? v)
[[] []]
[(subvec v 0 idx) (subvec v idx)]))
(defn split-at-max [xs]
(let [m-el (reduce-kv
(fn [max k v]
(if (<= v (second max))
max
[k v])) [0 (first xs)] xs)]
(if (vector? xs)
(vec-split-at (-> m-el first inc) xs)
(split-at (-> m-el first inc) xs))))
(split-at-max [1 10 10 1])
对于向量应该是N + C
比较。其中C相对较小。
答案 3 :(得分:1)
如果您希望避免将Java方法引入Clojure世界,可以将indexOf()
方法替换为count
和take-while
的组合。
user> (def up5 [1 2 3 4 5 4 3 2 1])
#<Var@20c4449f: [1 2 3 4 5 4 3 2 1]>
user> (split-at (inc (count (take-while #(< % (apply max up5)) up5))) up5)
[(1 2 3 4 5) (4 3 2 1)]
但是,我更喜欢以前的解决方案,虽然这比基于索引的解决方案更长。
user> (let [x (apply max up5)
[lhs rhs] (split-with #(< % x) up5)]
[(conj (vec lhs) (first rhs)) (vec (next rhs))])
[[1 2 3 4 5] [4 3 2 1]]
答案 4 :(得分:1)
(defn split-at-max [v]
(when (seq v)
(let [m (apply max v)
point (inc (count (reduce (fn [a b] (if (> m b) (conj a b)
(reduced a))) [] v)))]
((juxt #(take point %) #(drop point %)) v))))
(split-at-max [1 2 9 2 -7 33 3 4 53 1 22 4 -44 444 3 2 3 0 -21])
;;=> [(1 2 9 2 -7 33 3 4 53 1 22 4 -44 444) (3 2 3 0 -21)]
(split-at-max [])
;;=> nil
(split-at-max [26 27 28 29 30 31 32 33])
;;=> [(26 27 28 29 30 31 32 33) ()]
(split-at-max [33 32 31 30 29 28 27 26])
;;=> [(33) (32 31 30 29 28 27 26)]
;; works also with sets and lists:
(split-at-max '(1 2 9 2 -7 33 3 4 53 1 22 4 -44 444 3 2 3 0 -21))
;;=> [(1 2 9 2 -7 33 3 4 53 1 22 4 -44 444) (3 2 3 0 -21)]
(split-at-max '())
;;=> nil
(split-at-max (hash-set))
;;=> nil
(split-at-max (sorted-set))
;;=> nil
(split-at-max (sorted-set 1 2 9 2 -7 33 3 4 53 1 22 4 -44 444 3 2 3 0 -21))
;;=> [(-44 -21 -7 0 1 2 3 4 9 22 33 53 444) ()]
(split-at-max (hash-set 1 2 9 2 -7 33 3 4 53 1 22 4 -44 444 3 2 3 0 -21))
;;=> [(0 1 4 -21 33 22 -44 3 2 444) (-7 9 53)]
使用split-with
在最大点进行拆分的另一种类似方式(如果有可能有空集合,则首先需要在输入上执行seq
):
(let [v [1 2 9 2 -7 33 3 4 53 1 22 4 -44 444 3 2 3 0 -21]
m (apply max v)]
((juxt #(concat (first %) [(first (second %))]) #(rest (second %)))
(split-with (partial > m) v)))
;;=> [(1 2 9 2 -7 33 3 4 53 1 22 4 -44 444) (3 2 3 0 -21)]
答案 5 :(得分:1)
首先,鉴于Clojure cheatsheet中列出.indexOf
,我认为使用它是惯用的。
以下是另外两种选择:
这个类似于tnoda的第二个解决方案:
(let [[a b c] (partition-by #(< % (apply max up5) up5)]
[(concat a b) c])
;=> [(1 2 3 4 5) (4 3 2 1)]
下一个看起来更复杂,但在一个方面它更优雅:它延迟<
的效果以包含=
项,因此不需要使用{{1} }或conj
之后将concat
项目粘贴回第一个序列:
=
结果的第二个元素是(let [the-max (apply max up5)]
(loop [the-start []
the-rest up5
continue? true]
(if continue?
(let [this-one (first the-rest)]
(recur (conj the-start this-one)
(rest the-rest)
(< this-one the-max)))
[the-start the-rest])))
;=> [[1 2 3 4 5] (4 3 2 1)]
,顺便说一句。对于大多数目的而言,序列的类型无关紧要,但如果您真的需要向量,则可以对其应用clojure.lang.PersistentVector$ChunkedSeq
。同样对于我的第一个例子的结果。
答案 6 :(得分:1)
我从
开始(defn split-at-max [v]
(let [m (apply max v)
n (count (take-while #(> m %) v))]
(split-at (inc n) v)))
这很笨拙。我应该使用split-with
代替split-at
,而无需计算n
。但是,我们可以修改它以使用整个矢量:
(defn split-at-max [v]
(let [m (apply max v)
n (loop [i 0]
(if (= (v i) m) i (recur (inc i))))
n (inc n)]
[(subvec v 0 n) (subvec v n)]))
这避免了实现分割序列,因此使用起来更快。
loop
找到最大元素的第一个匹配项。从@Mars获取提示,我们可以使用Java ArrayList
的{{1}}方法:
indexOf
快速,简洁,清晰。