在 Clojure 中编写中缀宏时出现问题

The*_*Guy 0 clojure

我正在尝试编写 Clojure 中缀宏,但出现我不明白的编译错误。

它应该从正则数学表达式语法生成函数调用:

(macroexpand '(infix 3 * (2 + 1)))
;; => (* 3 (+ 2 1))
Run Code Online (Sandbox Code Playgroud)

我尝试将list语句转换为引用,但没有成功。

宏:

(defmacro functionize [macro]
  `(fn [& args#] (eval (cons '~macro args#))))

(defmacro infix
  ([n]
   (if (not (or (number? n) (fn? n)))
     `(~(apply (functionize infix) n))
     n))
  ([fir sec & res]
   (list sec (infix fir) (infix res))))
Run Code Online (Sandbox Code Playgroud)

错误:

1. Caused by java.lang.IllegalArgumentException
   Don't know how to create ISeq from: clojure.lang.Symbol
Run Code Online (Sandbox Code Playgroud)

错误发生在最后一行,第一次调用infix.

lee*_*ski 5

我可以在您的代码中看到至少一个可能的错误:[fir sec & res]可能应该是[fir sec res],因为您需要第三个参数,而不是所有参数的列表。它仍然无法解决代码中的问题。主要的一个,是你想得太多了(和eval东西)

我可能会使用这样的东西:

(defmacro infix [n]
  (if (list? n)
    (let [[arg1 op arg2] n]
      `(~op (infix ~arg1) (infix ~arg2)))
    n))

user> (clojure.walk/macroexpand-all '(infix (1 + ((6 - (3 / 7)) * 3))))
;;=> (+ 1 (* (- 6 (/ 3 7)) 3))

user> (infix (1 + ((6 - (3 / 7)) * 3)))
;;=> 124/7
Run Code Online (Sandbox Code Playgroud)

更新

要省略括号,您可以这样更新它:

(defn unwrap-arg [restargs]
  (if (= 1 (count restargs))
    (first restargs)
    restargs))

(defmacro infix [n]
  (if (list? n)
    (let [[arg1 op & arg2] n]
      `(~op (infix ~arg1) (infix ~(unwrap-arg arg2))))
    n))

user> (clojure.walk/macroexpand-all '(infix (1 + 2 + (10 - (4 / 10)) + (4 * 5))))
;;=> (+ 1 (+ 2 (+ (- 10 (/ 4 10)) (* 4 5))))

user> (infix (1 + 2 + (10 - (4 / 10)) + (4 * 5)))
;;=> 163/5
Run Code Online (Sandbox Code Playgroud)