调车场算法

发布于 2024-11-02 14:21:00 字数 2360 浏览 4 评论 0原文

我正在努力在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

我很OK 2024-11-09 14:21:00

我实际上并不知道 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. If stmt has no spaces, i.e. "3+5", you get a StackOverflowError, but that's progress, I guess.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文