Clojure,扩展为使用代码执行列表的宏 [英] Clojure, macro that expands to execute a list with code

查看:102
本文介绍了Clojure,扩展为使用代码执行列表的宏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,所以我有这个宏,应该采取不同数量的参数,然后执行与try和catch。我假设如果参数列表 arg-list 大于2那么列表中的第一个元素是一个绑定,像这样 [a 0 ] 。因此 arg-list 可能如下所示:([s(FileReader。(File。text.txt))](。 )



这是我想出来的:

 (defmacro safe [& arg-list](list'if(list'<(list'count arg-list)'2)
args list(list'catch'Exception'e'e))
(list'do(list'eval arg-list) list'catch'Exception'e'e)))))



我一直在努力得到这个像现在连续两天工作,但它从来没有工作。当我尝试这个宏与例如:

 (safe(+ 2 3))

我得到这个错误:

  ClassCastException java.lang.Long不能转换为clojure.lang.IFn user / eval91(NO_SOURCE_FILE:100)



<

解决方案

我只是在Clojure工作四天,如果我的代码不好,请原谅我。为了开始我建议你阅读clojure的宏语法。



第一件事,这是你的宏。

 (defmacro safe [bindings?& forms] 
(let [bindings(if(and(even?(count bindings?))如果绑定形式(cons绑定?形式))
except`(catch Exception e#e#)]
(如果绑定
`(let〜bindings(try〜@ forms〜except))
`(try〜@ forms〜except))))


Clojure的(let)宏需要一个具有偶数个参数的向量,支持一些名为解构的真正有趣的行为。为了这个宏的目的,我假设任何有效的绑定参数将首先是一个向量,第二个是偶数长度。 (let)的评估将执行这个相同的检查,但是这个宏必须这样做,因为第一个表单不是绑定,而是一个要评估的表单,并且在这种情况下应该表现出不同的行为。



对于宏本身,我使用(let)来处理参数,符号 bindings 的结合以及如果存在结合载体,则取结合载体。 表单从参数中的初始绑定(clojure允许你这样做)重新定义到受 bindings code>是您希望在包含错误的环境中执行的整个表单序列。 除了符号真的不被调用,它只是为了避免在每个扩展情况下重复(catch)形式的代码重复。



我使用的符号`(称为反引号或反引号)'在这里与普通引号(')相同,除了clojure允许我使用反向引用形式而不是引用形式中的宏扩展语法。宏语法包含〜(unquote)运算符和〜@(insert(unquote))uperator。使用这三个符号,我已经定义了两个期望的情况,let绑定形式,我插入绑定形式和要尝试的形式和简单的尝试只有case。



条件可以消除产生

 (defmacro safe [绑定?)
(let [bindings(if(and(even?(count bindings?))(vector?bindings?))
bindings?[])

除了`(catch Exception e#e#)]
`(let〜bindings(try〜@ forms〜except)) ))

但是,当没有绑定表单时,你有一个多余的>

Ok so I have this macro that is supposed to take a varying number of arguments and then execute them with try and catch. I am assuming that if the argument list arg-list is bigger then 2 then the first element in the list is a binding, like this [a 0] for example. So arg-list may look like this: ([s (FileReader. (File. "text.txt"))] (. s read)).

This is what I've come up with:

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

I have been struggling to get this to work for like two straight days now, but it never works. When I try this macro with for example this:

(safe (+ 2 3))

i get this error:

ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn  user/eval91 (NO_SOURCE_FILE:100)

I have only been working with Clojure for four days so forgive me if my code is bad.

解决方案

Well... for a start I suggest you read up on clojure's macro syntax. I'll provide a bit of a primmer here but I'm not going to go into depth.

First things first, here's your macro.

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

And now for a walk through.

Clojure's (let) macro demands a vector with an even number of arguments and supports some really interesting behavior called destructuring. For the purposes of this macro, I assume that any valid binding argument will first be a vector and second be of even length. The evaluation of (let) will perform this same check, but this macro must do it as it's possible that the first form is not a binding but a form to be evaluated and should exhibit different behavior in that case.

As to the macro itself, I use a (let) to process the arguments, the symbol bindings serving the double purpose of indicating the presence of bindings as well as taking the binding vector if one is present. forms is re-defined from its initial binding in the arguments (clojure lets you do that) to a value which is impacted by that of bindings being the entire form sequence which you wish to execute in an error-contained environment. The except symbol really isn't called for, it's just to escape the code duplication of restating that (catch) form in each of the expansion cases.

The symbol ` (known as backquote or backtick) which I use is equivalent here to normal quote (') except that clojure allows me to use the macro expansion syntax within backquoted forms and not quoted forms. The macro syntax contains the ~ (unquote) operator and the ~@ (insert (unquote)) uperator. Using these three bits of notation I've defined both desired cases, the let with a binding form where I insert the binding form and the forms to be tried and the simple try only case.

The conditional could be eliminated to produce

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

but then you have a superfluous (let) when there is no binding form.

这篇关于Clojure,扩展为使用代码执行列表的宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆