如何编写返回相邻对列表的 Clojure 函数?

Bry*_*yan 4 clojure

我正在尝试编写一个函数adjacents,该函数返回序列相邻对的向量。所以(adjacents [1 2 3])会回来[[1 2] [2 3]]

(defn adjacents [s]
  (loop [[a b :as remaining] s
          acc []]
    (if (empty? b)
      acc
      (recur (rest remaining) (conj acc (vector a b))))))
Run Code Online (Sandbox Code Playgroud)

我当前的实现适用于字符串序列,但对于整数或字符,REPL 会输出此错误:

IllegalArgumentException Don't know how to create ISeq from: java.lang.Long  clojure.lang.RT.seqFrom (RT.java:494)
Run Code Online (Sandbox Code Playgroud)

A. *_*ebb 5

这里的问题是在第一次评估循环(adjacents [1 2 3])a必然要1b2。然后你问是否bempty?。但empty?适用于序列并且b不是序列,它是Long,即2。您可以在此案例中使用的谓词是nil?

user=> (defn adjacents [s]
  #_=>   (loop [[a b :as remaining] s acc []]
  #_=>     (if (nil? b)
  #_=>       acc
  #_=>       (recur (rest remaining) (conj acc (vector a b))))))
#'user/adjacents
user=> (adjacents [1 2 3 4 5])
[[1 2] [2 3] [3 4] [4 5]]
Run Code Online (Sandbox Code Playgroud)

但是,正如@amalloy 指出的那样,如果您nil的数据中有合法的s ,这可能无法给出预期的结果:

user=> (adjacents [1 2 nil 4 5])
[[1 2]]
Run Code Online (Sandbox Code Playgroud)

有关使用列表的建议实现,请参阅他的评论。

请注意,partition可以使用Clojure来完成这项工作,而无需定义您自己的:

user=> (partition 2 1 [1 2 3 4 5])
((1 2) (2 3) (3 4) (4 5))
user=> (partition 2 1 [1 2 nil 4 5])
((1 2) (2 nil) (nil 4) (4 5))
Run Code Online (Sandbox Code Playgroud)

  • 测试 b 的 `nil?` 是灾难性的,因为现在你不能计算 `(adjacents '[xy nil 5 6 7])` 或类似的。相反,您需要使用列表,例如通过`(loop [[x & xs] 剩余] (if xs (let [y (first xs)] ...)))`。 (2认同)