几种符号

 

'  ~  ` @ 这些符号在Clojure中的含义

`表示syntax quote,'表示quote,~表示unquote,~@表示unquote splicing,详细说明下。如果某段代码前面加了'就表示这段代码被quote而不会去求值了,而`的syntax quote则表示会把相应的变量变成有namespace的形式,而~和`是搭配使用的,~必须在`的后面,并且~的数量不能超过`的数量,~是用来将变量的值替换到相应位置

 

'   不对表达式求值

` ( syntax-quote) 会对表达式解析

user=> '(foo bar)
(foo bar)
user=> `(foo bar)
(user/foo user/bar)

~ 对宏体内表达式计算, ~@ 对 seq 进行展开

 

user=> (defmacro dbg[x] `(let [x# ~x] (println '~x "=" x#) x#))
 
user=> (def x 5)
user=> (def lst '(a b c))
user=> `(fred x ~x lst ~@lst 7 8 :nine)
    (user/fred user/x 5 user/lst a b c 7 8 :nine)
 
user=> `(abc ~(symbol (str "i" "s" \- "cool")))
(user/abc is-cool)
 
user=>  `(max ~@(shuffle (range 10)))
(clojure.core/max 8 7 1 9 0 6 4 2 3 5)

 

相关实现逻辑在 https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LispReader.java

' 的实现
dispatchMacros['\''] = new VarReader();
 
public static class VarReader extends AFn{
     public Object invoke(Object reader, Object quote) {
          PushbackReader r = (PushbackReader) reader;
          Object o = read(r, true, null, true);
          return RT.list(THE_VAR, o);
     }
}
 
 
 
 
`的实现
macros['`'] = new SyntaxQuoteReader();
 
public static class SyntaxQuoteReader extends AFn
{
 // 代码省略........
}
 
~的实现
 
macros['~'] = new UnquoteReader();
 

static class UnquoteReader extends AFn{
     public Object invoke(Object reader, Object comma) {
          PushbackReader r = (PushbackReader) reader;
          int ch = read1(r);
          if(ch == -1)
               throw Util.runtimeException("EOF while reading character");
          if(ch == '@')
               {
               Object o = read(r, true, null, true);
               return RT.list(UNQUOTE_SPLICING, o);
               }
          else
               {
               unread(r, ch);
               Object o = read(r, true, null, true);
               return RT.list(UNQUOTE, o);
               }
     }

}

 

 我们想创建一个unqualified symbol 的时候,就会在symbol的后面添加#符号.

 

user=> `(x#)
(x__6__auto__)
 
;;;定义dbg
user=> (defmacro dbg[x] `(let [x# ~x] (println '~x "=" x#) x#))
#'user/dbg

user=> (defn pythag [x,y] (*(* x x) (* y y)))
#'user/pythag
user=> (pythag  5 6 )
900
user=> (defn pythag [x,y] (dbg(* (dbg (* x x)) (dbg (* y y)))))
#'user/pythag
user=> (pythag  5 6 )
(* x x) = 25
(* y y) = 36
(* (dbg (* x x)) (dbg (* y y))) = 900
900

 

  学习Macro,自我感觉比较好的学习方式就是看Clojure中宏的实现,尝试自己写一下.再看两个宏的例子:通过source函数查看->和 ->>的内部实现:
user=> ( -> 25 Math/sqrt int list)
(5)
user=> ( ->> 25 Math/sqrt int list)
(5)

user=> (source ->)
(defmacro ->
  "Threads the expr through the forms. Inserts x as the
  second item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  second item in second form, etc."
  {:added "1.0"}
  ([x] x)
  ([x form] (if (seq? form)
              (with-meta `(~(first form) ~x ~@(next form)) (meta form))
              (list form x)))
  ([x form & more] `(-> (-> ~x ~form) ~@more)))
nil
user=> (source ->>)
(defmacro ->>
  "Threads the expr through the forms. Inserts x as the
  last item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  last item in second form, etc."
  {:added "1.1"}
  ([x form] (if (seq? form)
              (with-meta `(~(first form) ~@(next form)  ~x) (meta form))
              (list form x)))
  ([x form & more] `(->> (->> ~x ~form) ~@more)))
nil
user=>
 

 传给宏的代码是不会求值的,这点和函数非常不同,函数传的参数都是先求值再去做函数运算,看下面的例子

 

    user=> (defn aa [] (println "aa") 1)
    #'user/aa
    user=> (defn bb [] (println "bb") 2)
    #'user/bb
    user=> (defn cc [c a b] (if c a b))
    #'user/cc
    user=> (defmacro dd [c a b] (if c a b))
    #'user/dd
    user=> (cc true (aa) (bb))
    aa
    bb
    1
    user=> (dd true (aa) (bb))
    aa
    1

 

 

 

因为函数的参数是先求值的,所以调用cc的时候bb也被运行,这不是我们所希望的,我们所希望的是像dd那样只去执行aa,而不去执行bb,所以这里就需要用宏了。

还有一个宏和函数的重要的不同是宏是在编译代码的时候运行的,运行一次之后就会把宏的返回值替换到代码的相应位置了。所以宏的话其实更像是元编程一类的东西,用代码去生成代码。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值