Skip to content

Instantly share code, notes, and snippets.

@ggeoffrey
Last active May 25, 2016 20:33
Show Gist options
  • Save ggeoffrey/6066f93555c022e25c0efa4113f579e9 to your computer and use it in GitHub Desktop.
Save ggeoffrey/6066f93555c022e25c0efa4113f579e9 to your computer and use it in GitHub Desktop.
This is clean and elegant … I guess… Way better then the first version.
(defn re-extract
"Try to re-find all given regexps until one matches.
nil if none matches."
[value & regexps]
(cond
(nil? value) nil ;; safe fallback
(empty? regexps) nil ;; no params or no more regexps to try
:else (let [pattern (first regexps) ;; take the first one
extracted (re-find pattern value)] ;; search for it
(cond
(string? extracted) extracted ;; a string mean "found"
:else ;; tail-call recurtion on the rest of the list
(recur value (rest regexps))))))
(defmacro safe-call
"Simply wrap the call in a try/catch block.
Return nil if something horrible happens."
[f body]
`(try
(~f ~body)
(catch Exception e#
nil)))
(defmacro with-parse
"Take a list of pairs like:
[value1 parsing-function-1
value2 parsing-function-2]
and create a binding like:
[value1 (parsing-function-1 value1)
value2 (parsing-function-2 value2)] "
[list body]
(let [pairs (partition 2 list) ;; make pairs of '(symbol symbol)
application (into [] ;; put into a vector
;; the flattened mapping of 'pairs'
(mapcat (fn [[value# function#]]
;; return a vector for the let binding
[value# `(safe-call ~function# ~value#)])
pairs))]
;; write to the AST
`(let ~application ~body)))
;; Optional
(defmacro parsing-with
"Shorthand for with-parse when there's only one parsing function."
[function values body]
;; create pairs like (interpose function values)
;; but for the last value too
(let [bindings (into [] ;; into a vector
(mapcat ;; put the flattened
#(vector % function) ;; pairs
values))] ;; of values
;; write to AST
`(with-parse ~bindings ~body)))
(defn compare-by-numeric-priority
"Compare two strings by their numeric priority
if they both start by a number. Otherwise compare
by alphabetical order."
[str1 str2]
(let [patterns [#"^\d+\.\d+" #"^\d+"] ;; what is a number?
num1 (apply re-extract str1 patterns) ;; try to find it in the 1st
num2 (apply re-extract str2 patterns)] ;; and in the second
(cond
(or (nil? num1) ;; if one of them doesn't start with a number
(nil? num2)) (compare str1 str2) ;; simply compare them
:else ;; by using the parsed version
(parsing-with Double/parseDouble
[num1 num2]
;; compare them
(compare num1 num2)))))
(def list-of-headers '("foo" "bar" "3.bazz" "3.2 bazz2" "a"))
(sort compare-by-numeric-priority list-of-headers)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment