Clojure语言十三: 宏的基本概念

本文深入探讨Clojure语言中的宏,揭示其核心作用,包括创建自定义操作符、延迟求值特性以及构建领域特定语言(DSL)。通过实例展示了如何利用宏打印表达式并在执行前进行转换,同时对比了宏与函数的区别,强调宏在处理未求值参数的独特优势。此外,介绍了宏的基本语法结构,帮助读者更好地理解和运用Clojure的宏功能。
摘要由CSDN通过智能技术生成

宏的核心作用


视作一个简单的工具

和其他语言不一样的地方,lisp的宏可以随意创建operator,如下面介绍的when。实现很简单,毫不费力。

参数不求值

由于micro接收的参数可以很复杂,而且不求值,因此可以接收list,在内部转换,加工后再执行,也可以接收完全不是lisp的语法,在内部转换成clojure能执行的list。

运行并打印表达式

利用参数不求值的特性,可以编写一个宏,先打印参数传递进来的表达式,然后再运行

user=> (defmacro print-eval [expression] (do (println expression) expression))
#'user/print-eval
user=> (print-eval (+ 1 2))
(+ 1 2)
3


创建自己的DSL

如果将接收的参数固定为某种语法,这样就可以创建自己的DSL,而把clojure宏实现为DSL解析器。

宏与函数的区别

特点是允许在Clojure evaluate你的list之前,你能够像处理函数一样的处理它, 但是不evaluate。

如果你想把一个表达式作为参数传递给函数,并要求这个参数不求值,这是做不到的。比如下面的函数定义会报错:

user=> (def ignore-last-operand2
  [function-call]
  (butlast function-call))
  #_=>   #_=> 
user=> CompilerException java.lang.RuntimeException: Too many arguments to def, compiling:(/tmp/form-init3734404425586418360.clj:1:1) 


本意是ignore-last-operand2接收一个list参数,但是不求值,在body运行的时候才进行求值,而且要忽略list的最后一个symbol.


这种情况需要使用宏了。

user=> (defmacro ignore-last-operand
  [function-call]
  (butlast function-call))
  #_=>   #_=> #'user/ignore-last-operand
user=> (ignore-last-operand (+ 1 2 10))
3


和C的宏有点像,不过C的宏是在预编译阶段做文本替换,还是弱了点。不像lisp可以在运行时随意使用宏。


宏的语法

(宏名 描述字符串 {版本说明} [参数列表] (body1) (body2) (bodyN))

一个宏可以与多个body,这些body有点像C++里面的重载函数,根据参数数目的不同而决定运行哪一个body

比如0参数body可以写成这样

([] true)
1个参数写成这样

([x] x)

多个参数这样

([x & next]
   `(let [and# ~x]
      (if and# (and ~@next) and#))))

注意,& next 是获取余下的的参数, 因为&左边出现了一个x, 所以next被绑定为余下的参数。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值