说我有一个功能:
(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方法是什么?
答案 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
结合使用时,您可以为每个输入返回零个或多个元素作为结果序列。不需要第二个版本中的错误案例的空列表,但可能有助于清晰。