我正在尝试编写 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.
我可以在您的代码中看到至少一个可能的错误:[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)