Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure: O(1) function to check if sequence has exactly 1 element

Is there a fast way to check if a sequence has exactly 1 element in Clojure? Note that a sequence may contain nils.

If I'm reading the source correctly, calling count on a sequence takes O(n) time.

Alternate solution:

(and
  (not (empty? a-seq))
  (empty? (rest a-seq)))

Docs say that calling empty? on collection coll is the same as calling (not (seq coll)), but they don't specify its efficiency or what happens when you call empty? on a sequence. I tried to search the github repository for how empty? was implemented, but it was ignoring the question mark in searches and there was a ton of hits on "empty". I would imagine empty? and rest are O(1), but then again, count wasn't...

like image 973
Atte Juvonen Avatar asked Dec 25 '22 03:12

Atte Juvonen


2 Answers

Because

user=> (empty? (cycle [1]))
false

(the fact that the function terminates), I assume empty? evaluates in constant time, namely, (seq coll) initialises a sequence in constant time.

user=> (source empty?)
(defn empty?
  "Returns true if coll has no items - same as (not (seq coll)).
  Please use the idiom (seq x) rather than (not (empty? x))"
  {:added "1.0"
   :static true}
  [coll] (not (seq coll)))
nil

Your code does the thing rather well. Maybe I'd say:

user=> (defn single-elem? [s]
  #_=>   (and
  #_=>     (seq s)
  #_=>     (empty? (rest s))))
#'user/single-elem?

user=> (single-elem? [1])
true
user=> (single-elem? [1 2])
false
user=> (single-elem? [])
nil
user=> (single-elem? {:foo :bar})
true
user=> (single-elem? {:foo :bar :fez 42})
false
like image 115
mike3996 Avatar answered Dec 26 '22 16:12

mike3996


The following function was added in 1.9 (which is still alpha as of now):

(defn bounded-count
  "If coll is counted? returns its count, else will count at most the first n
  elements of coll using its seq"
  {:added "1.9"}
  [n coll]
  (if (counted? coll)
    (count coll)
    (loop [i 0 s (seq coll)]
      (if (and s (< i n))
        (recur (inc i) (next s))
        i))))

So I guess a (bounded-count 2 coll) should give you a constant time count for what you need.

like image 25
ClojureMostly Avatar answered Dec 26 '22 17:12

ClojureMostly