首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >计算算术表达式的后走

计算算术表达式的后走
EN

Stack Overflow用户
提问于 2019-02-14 08:36:54
回答 3查看 379关注 0票数 1

我试图使用Instaparse来制作一个简单的算术表达式计算器。解析器似乎工作得很好,但我不知道如何计算返回的嵌套向量。目前我使用的是后置行走,就像这样

代码语言:javascript
复制
(ns test5.core
  (:require [instaparse.core :as insta])
  (:require [clojure.walk  :refer [postwalk]])
  (:gen-class))


(def WS
  (insta/parser
    "WS = #'\\s+'"))


(def transform-options
  {:IntLiteral read-string})


(def parser
  (insta/parser
    "AddExpr = AddExpr '+' MultExpr
      | AddExpr '-' MultExpr
      | MultExpr

     MultExpr = MultExpr '*' IntLiteral
      | MultExpr '/' IntLiteral
      | IntLiteral

     IntLiteral = #'[0-9]+'"

    :auto-whitespace WS))


(defn parse[input]
  (->> (parser input)
       (insta/transform transform-options)))


(defn visit [node]
  (println node)
  (cond
    (number? node) node
    (string? node) (resolve (symbol node))
    (vector? node)
      (cond
        (= :MultExpr (first node)) (visit (rest node))
        (= :AddExpr (first node)) (visit (rest node))
        :else node)
    :else node))


(defn evaluate [tree]
  (println tree)
  (postwalk visit tree))


(defn -main
  [& args]
  (evaluate (parse "1 * 2 + 3")))

postwalk确实遍历了向量,但作为结果,我得到了嵌套列表,例如

代码语言:javascript
复制
((((1) #'clojure.core/* 2)) #'clojure.core/+ (3))
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-02-14 09:35:24

使用org.clojure/core.match。根据当前语法,可以将评估函数编写为:

代码语言:javascript
复制
(defn eval-expr [expr]
  (match expr
         [:MultExpr e1 "*" e2] (* (eval-expr e1)
                                  (eval-expr e2))
         [:MultExpr e1 "/" e2] (/ (eval-expr e1)
                                  (eval-expr e2))
         [:AddExpr e1 "+" e2] (+ (eval-expr e1)
                                 (eval-expr e2))
         [:AddExpr e1 "-" e2] (- (eval-expr e1)
                                 (eval-expr e2))
         [:MultExpr e1] (eval-expr e1)
         [:AddExpr e1] (eval-expr e1)
         :else expr))

并以下列方式进行评价:

代码语言:javascript
复制
(-> "1 * 2 + 3"
    parse
    eval-expr)
;; => 5
票数 2
EN

Stack Overflow用户

发布于 2019-02-14 09:52:37

这正是我第一次创建Tupelo森林库的原因。

请看2017年克鲁格·康吉的谈话

我启动了这里有一些医生。您还可以看到活生生的例子

更新

下面是如何使用Tupelo森林库来完成这项工作:

首先,使用Hiccup格式定义抽象语法树(AST)数据:

代码语言:javascript
复制
  (with-forest (new-forest)
    (let [data-hiccup      [:rpc
                            [:fn {:type :+}
                             [:value 2]
                             [:value 3]]]
          root-hid         (add-tree-hiccup data-hiccup)

其结果是:

代码语言:javascript
复制
(hid->bush root-hid) => 
[{:tag :rpc}
 [{:type :+, :tag :fn}
  [{:tag :value, :value 2}]
  [{:tag :value, :value 3}]]]

演示walk-tree如何使用“显示拦截器”工作

代码语言:javascript
复制
  disp-interceptor {:leave (fn [path]
                             (let [curr-hid  (xlast path)
                                   curr-node (hid->node curr-hid)]
                               (spyx curr-node)))}
  >>        (do
              (println "Display walk-tree processing:")
              (walk-tree root-hid disp-interceptor))

其结果是:

代码语言:javascript
复制
Display walk-tree processing:
curr-node => {:tupelo.forest/khids [], :tag :value, :value 2}
curr-node => {:tupelo.forest/khids [], :tag :value, :value 3}
curr-node => {:tupelo.forest/khids [1037 1038], :type :+, :tag :fn}
curr-node => {:tupelo.forest/khids [1039], :tag :rpc}

然后定义操作符和拦截器来转换像(+ 2 3) => 5这样的子树。

代码语言:javascript
复制
  op->fn           {:+ +
                    :* *}
  math-interceptor {:leave (fn [path]
                             (let [curr-hid  (xlast path)
                                   curr-node (hid->node curr-hid)
                                   curr-tag  (grab :tag curr-node)]
                               (when (= :fn curr-tag)
                                 (let [curr-op    (grab :type curr-node)
                                       curr-fn    (grab curr-op op->fn)
                                       kid-hids   (hid->kids curr-hid)
                                       kid-values (mapv hid->value kid-hids)                                           
                                       result-val (apply curr-fn kid-values)]
                                   (set-node curr-hid {:tag :value :value result-val} [])))))}  
  ]  ; end of let form

; imperative step replaces old nodes with result of math op
(walk-tree root-hid math-interceptor)

然后,我们可以显示修改后的AST树,它包含(+ 2 3)的结果

代码语言:javascript
复制
(hid->bush root-hid) => 
[{:tag :rpc} 
  [{:tag :value, :value 5}]]

你可以请看这里的实时代码

票数 1
EN

Stack Overflow用户

发布于 2019-02-14 14:00:52

这不使用Instaparse或clojure.walk,但这里有一些仅使用reduce来评估infix数学的东西

代码语言:javascript
复制
(defn evaluate
  "Evaluates an infix arithmetic form e.g. (1 + 1 * 2)."
  [e]
  (let [eval-op (fn [op a b]
                  (let [f (resolve op)]
                    (f a b)))]
    (reduce
      (fn [[v op] elem]
        (cond
          (coll? elem)
          (if op
            [(eval-op op v (first (evaluate elem))) nil]
            [(first (evaluate elem)) nil])

          (and op (number? elem))
          [(eval-op op v elem) nil]

          (number? elem)
          [elem nil]

          (symbol? elem)
          [v elem]

          :else
          (throw (ex-info "Invalid evaluation" {:v v :op op :elem (type elem)}))))
      [0 nil]
      e)))

(first (evaluate (clojure.edn/read-string "(1 * 2 + 3)")))
=> 5
(first (evaluate (clojure.edn/read-string "(1 * 2 + (3 * 5))")))
=> 17

这需要输入字符串来表示有效的Clojure列表。我还具有分组乘法/除法的功能:

代码语言:javascript
复制
(defn pemdas
  "Groups division/multiplication operations in e into lists."
  [e]
  (loop [out []
         rem e]
    (if (empty? rem)
      (seq out)
      (let [curr (first rem)
            next' (second rem)]
        (if (contains? #{'/ '*} next')
          (recur (conj out (list curr next' (nth rem 2)))
                 (drop 3 rem))
          (recur (conj out curr) (rest rem)))))))

(pemdas '(9.87 + 4 / 3 * 0.41))
=> (9.87 + (4 / 3) * 0.41)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54686154

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档