Clojure压缩矢量

时间:2010-10-13 11:51:14

标签: clojure

我正在尝试找到一种“压缩”矢量的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的练习。

感谢。

4 个答案:

答案 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确保向量大小合适。