Skip to content

Instantly share code, notes, and snippets.

@embeepea
Last active August 29, 2015 14:15
Show Gist options
  • Save embeepea/7839393104502294475d to your computer and use it in GitHub Desktop.
Save embeepea/7839393104502294475d to your computer and use it in GitHub Desktop.
Clojure Needle Search
(defmacro if-nonneg [a b]
"Eval the 1st arg, and if it's nonnegative, return it without evaling the 2nd arg.
If the 1st arg evals to negative, eval and return the value of the 2nd arg."
`(let [v# ~a] (if (>= v# 0) v# ~b)))
(defn inc-if-nonneg [x]
"Return a value that is 1 more than x if x is nonnegative; otherwise return x."
(if (>= x 0) (inc x) x))
(defn find-needle-end [n h]
"Find the END of the first occurrence of needle n in haystack h -- i.e. the
index of the position in h immediately after the last character in the first
occurrence of n. If n does not appear in h, return -1."
(cond
(empty? n) 0 ; empty needle found at front of haystack (also works for empty haystack)
(empty? h) -1 ; empty haystack contains no (nonempty) needles
; if this :else clause is invoked, we know that neither n nor h is empty:
:else (if-nonneg
(if (= (first n) (first h)) ; if first chars match...
; search for end of needle tail in haystack tail, and if found,
; return that position + 1
(inc-if-nonneg (find-needle-end (rest n) (rest h)))
-1)
; otherwise, search for (entire) needle in haystack tail
(inc-if-nonneg (find-needle-end n (rest h))))))
(defn clamp [x v]
"Clamp all negative values to v -- i.e. returns x if x >= 0, otherwise returns v."
(if (< x 0) v x))
(defn find-needle [n h]
"Return the index of the (beginning of the) first occurrence of needle n
in haystack h. If n does not appear in h, return -1."
(clamp (- (find-needle-end n h) (count n)) -1))
(defn run-tests []
[ ;0123456789
(= 0 (find-needle "ha" "hahaystack"))
(= 2 (find-needle "hay" "hahaystack"))
(= -1 (find-needle "halt" "hahaystack"))
(= 3 (find-needle "ayst" "hahaystack"))
(= 5 (find-needle "stack" "hahaystack"))
(= -1 (find-needle "stacky" "hahaystack"))
(= -1 (find-needle "acky" "hahaystack"))
(= 0 (find-needle "haha" "hahaystack"))
(= 2 (find-needle "hays" "hahaystack"))
(= 2 (find-needle "hayst" "hahaystack"))
(= -1 (find-needle "haste" "hahaystack"))
(= 9 (find-needle "k" "hahaystack"))
(= 0 (find-needle "" "hahaystack"))
(= 0 (find-needle "hahaystack" "hahaystack"))
(= -1 (find-needle "hahaystackify" "hahaystack"))
(= -1 (find-needle "needle" "hahaystack"))
(= 2 (find-needle "haystack" "hahaystack"))
(= 0 (find-needle "" ""))
(= -1 (find-needle "needle" ""))
])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment