我正致力于在 Clojure 中实现一个中缀计算器,首先是我实现 Dijkstra 的 Shutting-yard 算法。我以为我把它弄得很好,但在开玩笑,它似乎根本不能很好地处理操作员。呼唤(shunting-yard "3 + 5")
=>
(\3)
。就这样。有人可以告诉我在这里处理操作员字符有什么问题吗?
(import '(java.lang.Character))
(defn operator? [sym]
"Determines if a given token is a mathematical operator."
(some #{sym} '(\+ \- \* \/ \% \^ \!)))
(defn associativity-of [operator]
"Determines the associativity of a given mathematical operator."
(if (some #{operator} '(\+ \- \* \/ \%))
'left
'right))
(defn precedence-of [operator]
"Determines the precedence of a given mathematical operator."
(case operator
(\+ \-) 2
(\* \/ \%) 3
(\^ \!) 4
0))
(defn operator-actions [stmt stack]
"Actions taken when the next token in the stmt is an operator."
(let [token-prec (precedence-of (first stmt))
token-assoc (associativity-of (first stmt))
stack-oper (first stack)
stack-prec (precedence-of stack-oper)]
(cond (or (and (= token-assoc 'left)
(<= token-prec stack-prec))
(and (= token-assoc 'right)
(< token-prec stack-prec)))
(cons stack-oper (shunt stmt (rest stack)))
:else (shunt (rest stmt) (cons (first stmt) stack)))))
(defn stack-operations [stack]
"Actions to take if (nil? stmt)"
(comment "If a left paren is found on the stack, it means
that there was no right paren to match it, and
therefore the statement had unbalanced parentheses.")
(cond (and (not (nil? stack))
(= (first stack) \()) (print "Unbalanced parentheses.\n")
(nil? stack) ()
:else (cons (first stack) (stack-operations (rest stack)))))
(defn shunt [stmt stack]
(cond (empty? stmt) (stack-operations stack)
(Character/isDigit (first stmt)) (cons (first stmt)
(shunt (rest stmt) stack))
(operator? (first stmt)) (operator-actions stmt stack)
(= (first stmt) \() (recur (rest stmt) (cons (first stmt) stack))
(= (first stmt) \)) (if (= (first stack) \()
(recur (rest stmt) (rest stack))
(cons (first stack) (shunt stmt (rest stack))))))
(defn shunting-yard [stmt]
(shunt stmt ()))