7.2 编写控制流宏
实现unless功能,与if相反,条件不成立时执行unless后代码。
先编写函数来实现:
user=> (defn unless [expr form] (if expr nil form))
验证一下,当表达式expr为false时正确执行
user=> (unless false (println "this should print"))
this should print
但是当expr为true时,也会执行unless后的代码
user=> (unless true (println "this should not print"))
this should not print
原因分析:
Clojure在将参数传递给函数之前,先计算所有参数值,因此就在判断前就执行了println函数。
宏可以解决这个问题,它们不会立即计算参数值。当Clojure遇到宏时,它分两步进行处理:
(1) 首先展开(或者说是执行)宏,将结果替换到原程序中,这个阶段被称为宏展开期
(2) 然后进行正常的编译,这个阶段被称为编译期
宏的定义通过defmacro关键字:
(defmacro name doc-string? attr-map? [params*] body)
使用宏实现unless功能
user=> (defmacro unless [expr form] (list 'if expr nil form))
验证一下
user=> (unless false (println "this should print"))
this should print
说明:执行过程中,Clojure会自动将unless形式展开为:
(if false nil (println "this should print"))
user=> (unless true (println "this should not print"))
nil
完全实现了unless功能。
特殊形式,设计模式和宏
Clojure在代码上没有特殊语法,它的代码有数据结构组成,也就是说数据即代码。而Java中有很多特殊形式,如在Java中实现unless功能,我们只能通过下面的语句来模拟:
if (!something) ...
Java中将一些常用的解决办法称为设计模式,然而设计模式不是一个完成的设计不能直接转换为代码。而Clojure的宏提供了一个间接的层可以自动完成重复出现模式的公共部分,与代码即数据的特性一起,使得你可以重写你的语言从而在运行中封装模式。
展开宏
函数macroexpand-1将显示宏展开的结果:
(macroexpand-1 form),如:
user=> (macroexpand-1 '(unless false (println "this should print")))
(if false nil (println "this should print"))
有时宏展开后还是宏,此时Clojure将继续展开直到只有正常代码为止。使用macroexpand函数可以查看递归展开结果:
(macroexpand form),如:
user=> (macroexpand '(.. arm getHand getFinger))
(. (. arm getHand) getFinger)
when和when-not
(when test & body)
(when-not test & body)
when-not宏的定义为:
(defmacro when-not [test & body]
&nbps; (list 'if test nil (cons 'do body)))
示例:
user=> (when-not false (println "this") (println "and also this"))
this
and also this
实现unless功能,与if相反,条件不成立时执行unless后代码。
先编写函数来实现:
user=> (defn unless [expr form] (if expr nil form))
验证一下,当表达式expr为false时正确执行
user=> (unless false (println "this should print"))
this should print
但是当expr为true时,也会执行unless后的代码
user=> (unless true (println "this should not print"))
this should not print
原因分析:
Clojure在将参数传递给函数之前,先计算所有参数值,因此就在判断前就执行了println函数。
宏可以解决这个问题,它们不会立即计算参数值。当Clojure遇到宏时,它分两步进行处理:
(1) 首先展开(或者说是执行)宏,将结果替换到原程序中,这个阶段被称为宏展开期
(2) 然后进行正常的编译,这个阶段被称为编译期
宏的定义通过defmacro关键字:
(defmacro name doc-string? attr-map? [params*] body)
使用宏实现unless功能
user=> (defmacro unless [expr form] (list 'if expr nil form))
验证一下
user=> (unless false (println "this should print"))
this should print
说明:执行过程中,Clojure会自动将unless形式展开为:
(if false nil (println "this should print"))
user=> (unless true (println "this should not print"))
nil
完全实现了unless功能。
特殊形式,设计模式和宏
Clojure在代码上没有特殊语法,它的代码有数据结构组成,也就是说数据即代码。而Java中有很多特殊形式,如在Java中实现unless功能,我们只能通过下面的语句来模拟:
if (!something) ...
Java中将一些常用的解决办法称为设计模式,然而设计模式不是一个完成的设计不能直接转换为代码。而Clojure的宏提供了一个间接的层可以自动完成重复出现模式的公共部分,与代码即数据的特性一起,使得你可以重写你的语言从而在运行中封装模式。
展开宏
函数macroexpand-1将显示宏展开的结果:
(macroexpand-1 form),如:
user=> (macroexpand-1 '(unless false (println "this should print")))
(if false nil (println "this should print"))
有时宏展开后还是宏,此时Clojure将继续展开直到只有正常代码为止。使用macroexpand函数可以查看递归展开结果:
(macroexpand form),如:
user=> (macroexpand '(.. arm getHand getFinger))
(. (. arm getHand) getFinger)
when和when-not
(when test & body)
(when-not test & body)
when-not宏的定义为:
(defmacro when-not [test & body]
&nbps; (list 'if test nil (cons 'do body)))
示例:
user=> (when-not false (println "this") (println "and also this"))
this
and also this