Clojure压缩向量

4

我正在尝试寻找一种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,但内部结果应该是一个向量。
我怀疑这个函数会涉及到对nil值的过滤,并使用into将其添加到与原始向量相同长度的nil向量中,但我不确定如何将结果缩减回原始长度。我知道如何手动完成这个过程,但我认为Clojure能够在一行代码中完成它。
我正在考虑写一个Bejeweled玩家作为学习Clojure的练习。
谢谢。

如果您只允许将值向右移动... - Hamish Grubijan
我想到的数据结构是一个包含8个向量的向量。每个内部向量代表宝石的一列。"apply-move"函数将用nil值替换消失的宝石。然后我会使用“压缩”函数将nil值移动到顶部,再用宝石重新填充(我已经有一个完成这个任务的函数)。 - Ralph
4个回答

2
也许可以这样做:
(defn shift-nils-left
   "separate nil values" 
    [s] 
    (let [s1 (vec (flatten (clojure.contrib.seq/separate nil? s)))] 
        (list (not (= s s1)) s1)))

我应该像dreish一样使用'not='。;-) - Thomas Wagner

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))))

编辑:所以,与Thomas发布的内容相同,但我不会使用flatten,以防您最终使用某种seqable对象来表示宝石。


我正在考虑在棋盘中使用向量嵌套的方式。每个内部向量将代表一个列(让宝石可以下落)。我将用 nil 替换移除的宝石,然后使用“左移 nil”函数来将它们“过滤”到列的顶部。然后我会用新的随机宝石替换每个 nil。这些宝石本身可能只是红色、白色等关键字。 - Ralph
顺便说一句,我应该知道在Clojure社区中会有人比我更早想到分区问题 :-) - Ralph

2

稍微低层次一些的方法。它只遍历了输入序列一次以及非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)]))

1
(def v [1 2 nil 4 5 nil 7 8] )

(apply vector (take 8 (concat (filter identity v) (repeat nil))))

这将使用filter创建向量中非空值的序列,然后在序列末尾添加nil。这将给出所需的值作为序列,然后将它们转换为向量。take 8确保向量的大小正确。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接