删除集合中的相邻和相等元素

时间:2014-09-27 19:15:22

标签: collections clojure transformation

说我有一个功能:

(defn get-token [char]

  (defn char->number? []
    (re-matches #"\d" (str char)))

  (defn whitespace? []
    (or
      (= \space char)
      (= \newline char)))

  (defn valid-ident-char? []
    (re-matches #"[a-zA-Z_$]" (str char)))

  (cond
    (whitespace?)
    nil

    (= \' char)
    :quote

    (= \) char)
    :rparen

    (= \( char)
    :lparen

    (char->number?)
    :integer

    (valid-ident-char?)
    :identifier

    :else
    (throw (Exception. "invalid character"))))

当我运行此函数时,例如,字符串"(test 1 2)",我得到每个字符的符号列表:

'(:lparen :identifier :identifier :identifier nil :integer nil :integer :rparen)

看到这不完全是我想要的,我正在尝试编写一个带有集合的函数,并将集合“浓缩”为组合相等的相邻元素

最后一个例子可能会这样做:

(defn combine-adjacent [coll]
  implementation...)

(->>
  "(test 1 2)"
  (map get-token)
  (combine-adjacent)
  (remove nil?))

; => (:lparen :identifier :integer :integer :rparen)

实现这一目标的惯用Clojure方法是什么?

3 个答案:

答案 0 :(得分:2)

Clojure 1.7将引入一个名为dedupe的新函数来完成这个:

(dedupe [0 1 1 2 2 3 1 2 3])
;= (0 1 2 3 1 2 3)

如果你准备使用1.7.0-alpha2,你今天就可以使用它。

实现依赖于传感器(dedupe在没有参数的情况下调用时产生传感器;一元过载简单地定义为(sequence (dedupe) coll)),因此它不会直接反向传输。 / p>

答案 1 :(得分:1)

不确定它是多么惯用,但是一种方法是使用partition-by将传入序列的元素分组到包含相同元素的子序列的列表中,然后使用map来获取每个列表中的第一个元素。

所以,在代码中

(defn combine-adjacent [input] 
     (->> input (partition-by identity) (map first)))

(defn combine-adjacent [input] 
    (->> (partition-by identity input) (map first))

应该有用。

答案 2 :(得分:1)

将物品与隔壁物品进行比较有几种技巧:

首先,我们可以将它与尾巴进行比较:

(defn combine-adjacent
  [s]
  (mapcat #(when (not= % %2) [%]) (rest s) s))

或者,我们可以采用两次序列,然后删除重复

(defn combine-adjacent
  [s]
  (mapcat (fn [[a b]] (if (not= a b) [a]) ())
          (partition 2 1[0 1 2 2 2 3 2 3])))

这两个都利用了concat的有用属性与map结合使用时,您可以为每个输入返回零个或多个元素作为结果序列。不需要第二个版本中的错误案例的空列表,但可能有助于清晰。