调车场算法
我正在努力在 Clojure 中实现一个中缀计算器,首先是我实现 Dijkstra 的分流场算法。我以为我已经把它写得很好了,但对我来说是个笑话,它似乎根本不能很好地处理操作员。调用(调车场“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 ()))
I'm working on implementing an infix-calculator in Clojure, which starts with me implementing Dijkstra's Shunting-yard Algorithm. I thought I had it down pretty well, but joke's on me, it doesn't seem to handle operators very well at all. Calling (shunting-yard "3 + 5")
=>
(\3)
. That's all. Could someone tell me what's wrong with my handling of operator characters here?
(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 ()))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我实际上并不知道 Shunting-Yard 算法(刚才在维基百科上节省了 2 分钟),但我看到的一个问题是
shunt
不处理空格。因此它读取您的\3
、递归并退出,因为下一个字符是\space
。如果stmt
没有空格,即"3+5"
,您会收到StackOverflowError
,但我想这就是进步。I don't actually know the Shunting-Yard algorithm (save 2 minutes in Wikipedia just now), but one issue I see is that that
shunt
doesn't handle spaces. So it reads your\3
, recurses, and exits since the next char is\space
. Ifstmt
has no spaces, i.e."3+5"
, you get aStackOverflowError
, but that's progress, I guess.