好的所以我有这个宏应该采用不同数量的参数然后用try和catch执行它们。我假设如果参数列表arg-list
大于2,则列表中的第一个元素是绑定,例如此[a 0]
。因此arg-list
可能如下所示:([s (FileReader. (File. "text.txt"))] (. s read))
。
这就是我想出来的:
(defmacro safe [& arg-list] (list 'if (list '< (list 'count arg-list) '2)
(list 'try (list 'eval arg-list) (list 'catch 'Exception 'e 'e))
(list 'do (list 'eval arg-list) (list 'try (list 'eval (list 'rest arg-list)) (list 'catch 'Exception 'e 'e)))))
我一直在努力让这个连续两天工作,但它永远不会奏效。当我尝试这个宏时,例如:
(safe (+ 2 3))
我收到此错误:
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval91 (NO_SOURCE_FILE:100)
我只与Clojure合作了四天,如果我的代码不好,请原谅我。
答案 0 :(得分:4)
嗯......首先我建议你阅读clojure的宏语法。我会在这里提供一些原点,但我不打算深入探讨。
首先,这是你的宏。
(defmacro safe [bindings? & forms]
(let [bindings (if (and (even? (count bindings?)) (vector? bindings?))
bindings? nil)
forms (if bindings forms (cons bindings? forms))
except `(catch Exception e# e#)]
(if bindings
`(let ~bindings (try ~@forms ~except))
`(try ~@forms ~except))))
现在可以继续。
Clojure(let)宏需要一个具有偶数个参数的向量,并支持一些名为destructuring的非常有趣的行为。出于这个宏的目的,我假设任何有效的绑定参数首先是一个向量,第二个是偶数长度。 (let)的评估将执行相同的检查,但是这个宏必须这样做,因为第一个表单可能不是绑定而是要评估的表单,并且在这种情况下应该表现出不同的行为。
对于宏本身,我使用(let)处理参数,符号bindings
用于指示绑定的存在以及绑定向量(如果存在)的双重目的。 forms
从参数中的初始绑定(clojure允许您这样做)重新定义为受bindings
影响的值,该值是您希望在错误中执行的整个表单序列 - 包含环境。实际上并没有要求except
符号,它只是为了避免在每个扩展案例中重复(catch)形式的代码重复。
我使用的符号`(称为反引用或反引号)在这里等同于普通引号('),除了clojure允许我在反引号形式而不是引用形式中使用宏扩展语法。宏语法包含〜(unquote)运算符和〜@(insert(unquote))uperator。使用这三个符号表示我已经定义了两个所需的情况,带有绑定表单的let,我插入绑定表单和要尝试的表单以及简单的try only case。
条件可以被删除以产生
(defmacro safe [bindings? & forms]
(let [bindings (if (and (even? (count bindings?)) (vector? bindings?))
bindings? [])
forms (if-not (empty? bindings)
forms (cons bindings? forms))
except `(catch Exception e# e#)]
`(let ~bindings (try ~@forms ~except))))
但是当没有绑定表格时你会有一个多余的(let)。
答案 1 :(得分:1)
您不需要eval
- 宏扩展的结果已经被评估。使用宏内部的语法引用最容易实现您想要的内容:
(defmacro safe [& args]
(if (< (count args) 2)
`(try ~@args (catch Exception e# e#))
`(let ~(first args)
(try ~@(rest args) (catch Exception e# e#)))))
(safe (+ 2 3)) => 5
(safe [x 3] (+ 2 x)) => 5
(safe (Integer/parseInt "a")) => #<NumberFormatException java.lang.NumberFormatException: For input string: "a">
您看到异常的原因是示例中的arg-list
将是一个表单列表,在您的情况下,该列表包含一个列表'(+ 2 5)
的项目。当您评估第一个项目是列表的列表时,首先评估内部表单,然后评估外部表单:
(eval '(+ 2 3)) => 5
(eval '((+ 2 3))) => (eval '(5)) => exception, because 5 is not a function.
您可以通过将(list 'eval arg-list)
更改为(list 'eval (first arg-list))
来修复您的宏。