1

好的,所以我有这个宏,它应该接受不同数量的参数,然后用 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 工作了四天,如果我的代码不好,请原谅我。

4

2 回答 2

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) 宏需要一个具有偶数个参数的向量,并支持一些非常有趣的行为,称为解构。出于这个宏的目的,我假设任何有效的绑定参数首先是一个向量,其次是偶数长度。(let) 的求值将执行相同的检查,但此宏必须执行此操作,因为第一个表单可能不是绑定而是要评估的表单,并且在这种情况下应该表现出不同的行为。

至于宏本身,我使用 (let) 来处理参数,该符号bindings具有双重目的,即指示绑定的存在以及获取绑定向量(如果存在)。forms从参数中的初始绑定(clojure 允许您这样做)重新定义为一个值,该值受bindings您希望在包含错误的环境中执行的整个表单序列的影响。except确实不需要该符号,它只是为了避免在每个扩展案例中重述该(捕获)形式的代码重复。

我使用的符号 `(称为反引号或反引号)在这里等同于普通引号 ('),除了 clojure 允许我在反引号形式而不是引用形式中使用宏扩展语法。宏语法包含 ~ (unquote) 运算符和 ~@ (insert (unquote)) 上运算符。使用这三位符号,我定义了两种所需的情况,带有绑定表单的 let 我插入绑定表单和要尝试的表单,以及简单的仅尝试情况。

可以消除条件以产生

(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)。

于 2013-01-10T19:42:46.453 回答
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在您的示例中将是一个表单列表,在您的情况下,它有一个项目是 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)).

于 2013-01-10T19:30:33.773 回答