Clojure语言十三: 宏

micro的核心作用

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

这样可以创建自己的DSL,而把clojure宏实现为DSL解析器。

micro与function的区别

特点是允许在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可以在运行时随意使用宏。

用macroexpand检查返回的list

上面的例子中,其实经过了去除最后一个symbol,然后再对余下的list求值的过程。

(+ 1 2 10) -> (+ 1 2) -> 3

要想知道最后求值结果出来前的list,可以使用macroexpand. 注意要配合使用‘ 表示不要求值。

user=> (macroexpand ‘(ignore-last-operand (+ 1 2 10)))
(+ 1 2)

这样就可以显示宏返回的list,而不是求值。

常用宏

-> 宏改变从右到左的阅读方式

(defn read-resource
  "Read a resource into a string"
  [path]
  (read-string (slurp (clojure.java.io/resource path))))

这个函数运算的时候会先从最右边的(因为也是最里面)的list开始求值,path -> (clojure...) -> (slurp...) -> (read-string ...), 然后往外层层求值。通过->宏可以将最先求值的放在最上面,写成这样。

(defn read-resource
  [path]
  (-> path
      clojure.java.io/resource
      slurp
      read-string))

每个人有不同的偏好,有些人还是会这样用,为了减少括号的数目和符合自己从左到右或者从上到下的阅读习惯。

binding宏

binding宏用来创建线程专属的变量,在多线程中很常用。

user=> (def ^:dynamic x 1)
user=> (def ^:dynamic y 1)
user=> (+ x y)
2

user=> (binding [x 2 y 3]
         (+ x y))
5

user=> (+ x y)
2

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-09-30 11:31:06

Clojure语言十三: 宏的相关文章

C语言中宏定义(#define)时do{}while(0)的价值(转)

C语言中宏定义(#define)时do{}while(0)的价值 最近在新公司的代码中发现到处用到do{...}while(0),google了一下,发现Stack Overflow上早有很多讨论,总结了一下讨论,加上自己的理解,do{...}while(0)的价值主要体现在: 1. 增加代码的适应性 下面的宏定义没有使用do{...}while(0) #define FOO(x) foo(x); bar(x); 这样宏定义,单独调用不会出现问题,例如: FOO(100) 宏扩展后变成: 1 f

c语言的宏的使用方法(转自他人)

C语言宏定义技巧 周四, 2008年 10月 09日 14:10 高级管理员 C/C++编程 - C语言基础   1,防止一个头文件被重复包含 #ifndef COMDEF_H #define COMDEF_H //头文件内容 #endif 2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植. typedef  unsigned char      boolean;     /* Boolean value type. */typedef  unsigned

单片机学习之:C语言基础——宏定义(#define)

例如: #define uint unsigned int //用uint来代替(表示)unsigned int,语句前有#,后面无须加分号“;” uint可为除关键字以外的任意字符,以方便书写.同理:#define uchar unsigned char 单片机学习之:C语言基础--宏定义(#define)

C语言中宏的使用(#,##,do…while(0)宏)

1.预定义宏的使用__FILE__,__FUNCTION__,__LINE__. #include <stdio.h> void fun(void) { char v1; short v2; int v3; long v4; printf("v1: %x\n",&v1); printf("v2: %x\n",&v2); printf("v3: %x\n",&v3); printf("v4: %x\n&

Clojure语言十四 常用宏

常用宏 infix宏解析人的四则运算书写格式并执行 (defmacro infix "Use this macro when you pine for the notation of your childhood" [infixed] (list (second infixed) (first infixed) (last infixed))) 这么用: (infix (1 + 1)) ; => 2 用macroexpand检查返回的list 上面的例子中,其实经过了去除最后一个

C语言中宏定义使用方法详解

C语言中的宏替换详解 首先看一个问题: #include <stdio.h> #define    PRINT_CLINE()    printf("%d", ______) int main(void) { PRINT_CLINE(); PRINT_CLINE(); return 0; } 在横线处填上适当的代码,使得上面这段代码的输出为34. 我想一般人看到这个问题的时候头脑里都没有明确的思路来解答这个它.我看到这个问题的时候想出了各种办法来解答它,最终还是没有通过编译

C语言高级宏技巧

特殊符号#.## (1)# When you put a # before an argument in a preprocessor  macro, the preprocessor turns that argument into a character array.  在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组 简化理解:#是“字符串化”的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串 #define ERROR_LOG(module) fprint

简单讲解C语言中宏的定义与使用

宏定义是预编译功能的一种, 预编译又称为预处理, 是为编译做的预备工作的阶段.处理#开头的指令, 比如拷贝 #include 包含的文件代码,#define宏定义的替换,条件编译等. 使用宏定义的好处:使用宏定义的好处:可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改.例如 π 这个常量,我们有时候会在程序的多个地方使用,如果每次使用都重新定义,一来比较麻烦,二来容易出错,所以我们可以把 π 做成宏定义来使用.   语法说明: (1)宏名一般用大写 (2)使用宏可提高程序的通用性

C语言之宏

所谓的宏就是一种预处理命令,什么是与处理呢?即在编译过程之前先对程序代码做出的必要的转换处理.宏有两个作用: 1.当遇到需要将程序某个特定的数量在程序中出现的所有实例通通加以修改时,程序只需改动一处即可. 2.大多数C语言函数调用时都会带来重大的系统开销,而宏看上去像一个函数却没有函数调用的开销. 宏虽然有优点,但是也有其自身的缺陷. 首先得明确宏只是简单地替换.例如 #define CHAR char* Int main() { Char a=’A’; CHAR  p1,p2; P1=&a;