特殊形式
阻止求值:quote
quote阻止Clojure表达式求值,例如:
(quote x)
;=x
(symbol? (quote x))
;=true
quote对应的reader语法为单引号(’),reader在求值到单引号后会把它解析为一个quote:
'x
;=x
任何Clojure形式都可以被quote,包括数据结构,例如:
'(+ x x)
;=(+ x x)
(list? '(+ x x))
;=true
代码块:do
do会依次求值你传进来的所有表达式,并且把最后一个表达式的结果作为返回值,例如:
(do
(println "hi")
(apply * [4 5 6]))
; hi
;=120
定义Var:def
def的作用是在当前命名空间里定义(或重定义)一个var(可以赋值也可以不赋值):
(def p "foo")
p
;="foo"
本地绑定:let
let定义本地绑定,以下面例子为例,在Java中:
public static double hypot (double x, double y){
final double x2 = x * x;
final double y2 = y * y;
return Math.sqrt (x2 + y2);
}
在Clojure中与之等价的是:
(defn hypot
[x y]
(let [x2 (* x y)
y2 (* y y)]
(Math/sqrt (+ x2 y2))))
上面的x2和y2在Java方法和Clojure函数里面的作用是一样的:给一个中间变量/值命名。
let定义的绑定有两个显著的特点:
- 所有本地绑定都是不可变的。可以在一个嵌套的let形式里面把一个本地绑定绑定到另外一个值,或者可以在一个绑定数组的后面把它绑定到别的值,但是没有办法改变一个let绑定的值。
- let的绑定数组在编译期可以对通用集合类型进行解构,利用”解构”,可以大大简化从绑定数组中抽取想要的数据的操作。
解构(let,第2部分)
Clojure访问一个集合中的多个数值的方法,以下面为例:
定义一个vector:
(def v [42 "foo" 99.2 [5 12]])
访问vector中元素的方法:
(first v)
;=42
(second v)
;="foo"
(last v)
;=[5 12]
(nth v 2)
;=99.2
(v 2)
;=99,2
(.get v 2)
;=99.2
- Clojure提供了简单的函数访问顺序集合的第一个,第二个以及最后一个值。
- 同时还提供了 nth 函数来返回顺序集合的指定下标的元素。
- Clojure 里面的 vector 本身也是函数,它接收数组下标作为参数,返回该下标中保存的元素。
- 所有的 Clojure 的顺序集合都实现了 java.util.List 这个接口,所以还可以使用List接口的 .get 方法来返回顺序集合中的元素。
let 支持两种类型的解构,对于顺序集合的解构以及对于 map 的解构。
顺序解构
顺序解构可以对任何顺序集合进行解构,包括:
- Clojure原生的list、vector以及seq。
- 任何实现了java.util.List 接口的集合(比如 ArrayList 和 LinkedList)。
- Java 数组。
- 字符串,对它解构的结果是一个个字符。
下面是一个基本的例子,解构上面的那个v:
最基本的顺序结构:
(def v [42 "foo" 99.2 [5 12]])
(let [[x y z] v]
(+ x z))
;=141.2
解构的形式还支持嵌套解构形式,如下:
(let [[x _ _ [y z]] v]
(+ x y z))
;=59
把解构形式和要被解构的集合摆在一起,如下所示:
[x _ _ [y z ]]
[42 "foo" 99.2 [5 12]]
顺序解构的两个特性:
保持 “剩下的” 元素
可以使用 & 符号来保持解构剩下的那些元素:
(let [[x & rest] v]
rest)
;=("foo" 99.2 [5 12])
保持被解构的值
可以在解构形式中指定 :as 选项来把被解构的原始集合绑定到一个本地绑定:
(let [[x _ z :as original-vector] v]
(conj original-vector (+ x z)))
;=[42 "foo" 99.2 [5 12] 141.2]
这里 original-vector 被绑定到未做修改的集合 v 。如果要解构的集合是一个函数 调用的返回值,同时又想保持对于这个集合的绑定,那么这个特性会很方便。
map 解构
map 解构和顺序解构在概念上是一样的: 通过解构形式从 map 中抽出一些元素。map 解构对下面几种数据结构有效。
- Clojure 原生的 hash-map、array-map,以及记录类型。
- 任何实现了 java.util.Map 的对象
- get 方法所支持的任何对象。如:
— Clojure 原生 vector
— 字符串
— 数组
最简单的map解构:
(def m {:a 5 :b 6
:c [7 8 9]
:d {:e 10 :f 11}
"foo" 88
42 false})
(let [{a :a b :b} m]
(+ a b))
;=11
map 解构中用做 key 的不止是关键字,可以是任何类型的值:
(let [{f "foo"} m]
(+ f 12))
;=100
(let [{v 42} m]
(if v 1 0))
;=0
如果 map 解构的 vector 、字符串或者数组,那么解构的 key 是数字类型的数组下标。如:
(let [{x 3 y 8} [12 0 0 -18 44 6 0 0 1]]
(+ x y))
;= -17
角标从 0 开始。
map 解构也可以处理内嵌 map:
(let [{{e :e} :d} m]
(* 2 e))
;=20