Skip to content

Instantly share code, notes, and snippets.

@nforgerit
Created November 24, 2017 09:59
Show Gist options
  • Save nforgerit/de84436d84fd1dcebd08a37ac1335ac2 to your computer and use it in GitHub Desktop.
Save nforgerit/de84436d84fd1dcebd08a37ac1335ac2 to your computer and use it in GitHub Desktop.
Clojure: Infix Calculator
(defn tokenize [expr]
(let [to-chars #(clojure.string/split (clojure.string/replace % " " "") #"")
is-digit? #(and % (re-find #"^\d+$" %))]
(reverse
(reduce
(fn [[t & ts :as tokens] token]
(if (and (is-digit? token) (is-digit? t))
(cons (str t token) ts)
(cons token tokens)))
'(), (to-chars expr)))))
(defn shunting-yard [tokens]
(let [ops {"+" 1, "-" 1, "*" 2, "/" 2}]
(flatten
(reduce
(fn [[rpn stack] token]
(let [less-op? #(and (contains? ops %) (<= (ops token) (ops %)))
not-open-paren? #(not= "(" %)]
(cond
(= token "(") [rpn (cons token stack)]
(= token ")") [(vec (concat rpn (take-while not-open-paren? stack))) (rest (drop-while not-open-paren? stack))]
(contains? ops token) [(vec (concat rpn (take-while less-op? stack))) (cons token (drop-while less-op? stack))]
:else [(conj rpn token) stack])))
[[] ()]
tokens))))
(defn rpn [tokens]
(let [ops {"+" +, "-" -, "*" *, "/" /}]
(first
(reduce
(fn [stack token]
(if (contains? ops token)
(cons ((ops token) (second stack) (first stack)) (drop 2 stack))
(cons (read-string token) stack)))
[] tokens))))
(-> "4 * (2+24) / 2 - 1"
tokenize
shunting-yard
rpn)
;; => 51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment