我正在尝试找到一种“压缩”矢量的Clojure惯用方法:
(shift-nils-left [:a :b :c :a nil :d nil])
;=> (true [nil nil :a :b :c :a :d])
(shift-nils-left [nil :a])
;=> (false [nil :a])
(shift-nils-left [:a nil])
;=> (true [nil :a])
(shift-nils-left [:a :b])
;=> (false [:a :b])
换句话说,我想将所有nil
值移动到向量的左端,而不更改长度。布尔值表示是否发生了任何移位。 “外部”结构可以是任何seq
,但内部结果应该是矢量。
我怀疑该函数将涉及filter
(在nil值上)和into
以添加到与原始长度相同的nil
s的向量,但我'我不知道如何将结果缩减回原来的长度。我知道如何“长手”,但我怀疑Clojure能够在一条线上完成。
我想要写一个宝石迷阵玩家作为练习Clojure的练习。
感谢。
答案 0 :(得分:2)
也许这样:
(defn shift-nils-left
"separate nil values"
[s]
(let [s1 (vec (flatten (clojure.contrib.seq/separate nil? s)))]
(list (not (= s s1)) s1)))
答案 1 :(得分:2)
我会这样写:
(ns ...
(:require [clojure.contrib.seq-utils :as seq-utils]))
(defn compress-vec
"Returns a list containing a boolean value indicating whether the
vector was changed, and a vector with all the nils in the given
vector shifted to the beginning."
([v]
(let [shifted (vec (apply concat (seq-utils/separate nil? v)))]
(list (not= v shifted)
shifted))))
编辑:所以,就像托马斯打败我发布的一样,但我不会使用flatten,以防你最终使用某种seqable对象代表珠宝。
答案 2 :(得分:2)
更低层次的方法。它只移动输入seq一次以及非nils矢量一次。两个更高级的方法遍历输入序列两次(对于nil?
和(complenent nil?)
)。 not=
在最坏情况下无移位时第三次遍历输入。
(defn compress-vec
[v]
(let [[shift? nils non-nils]
(reduce (fn [[shift? nils non-nils] x]
(if (nil? x)
[(pos? (count non-nils)) (conj nils nil) non-nils]
[shift? nils (conj non-nils x)]))
[false [] []] v)]
[shift? (into nils non-nils)]))
答案 3 :(得分:1)
(def v [1 2 nil 4 5 nil 7 8] )
(apply vector (take 8 (concat (filter identity v) (repeat nil))))
这将使用filter
在向量中创建一系列非零值,然后将nils附加到序列的末尾。这会将您想要的值作为序列提供,然后将它们转换为矢量。 take 8
确保向量大小合适。