Bryan
Bryan

Reputation: 193

How to write a Clojure function that returns a list of adjacent pairs?

I'm trying to write a function adjacents that returns a vector of a sequence's adjacent pairs. So (adjacents [1 2 3]) would return [[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))))))

My current implementation works for sequences of strings but with integers or characters the REPL outputs this error:

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

Upvotes: 4

Views: 777

Answers (2)

StCredZero
StCredZero

Reputation: 464

Here is my short answer. Everything becomes a vector, but it works for all sequences.

(defn adjacent-pairs [s] 
  {:pre [(sequential? s)]}
  (map vector (butlast s) (rest s)))

Testing:

user=> (defn adjacent-pairs [s] (map vector (butlast s) (rest s))) 
#'user/adjacent-pairs
user=> (adjacent-pairs '(1 2 3 4 5 6))
([1 2] [2 3] [3 4] [4 5] [5 6])
user=> (adjacent-pairs [1 2 3 4 5 6])
([1 2] [2 3] [3 4] [4 5] [5 6])
user=> 

This answer is probably less efficient than the one using partition above, however.

Upvotes: 3

A. Webb
A. Webb

Reputation: 26446

The problem here is in the first evaluation loop of (adjacents [1 2 3]), a is bound to 1 and b to 2. Then you ask if b is empty?. But empty? works on sequences and b is not a sequence, it is a Long, namely 2. The predicate you could use for this case here is 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]]

But, as @amalloy points out, this may fail to give the desired result if you have legitimate nils in your data:

user=> (adjacents [1 2 nil 4 5])
[[1 2]]

See his comment for suggested implementation using lists.

Note that Clojure's partition can be used to do this work without the perils of defining your own:

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

Upvotes: 5

Related Questions