Clojure API 文档¶

这是 Clojure 官方 API 文档 clojure.github.com/clojure 的中文翻译版本。
翻译工作目前正在进行中,欢迎加入: github.com/huangz1990/clojure_api_cn 。
目录:
clojure.core¶
->¶
将多个形式串连成一个表达式。
如果只有一个参数 x
和一个形式 form
,那么将 x
作为第二个项(item),插入到 form
当中,如果 form
不是一个列表,那么将 form
包裹到一个列表里面。[译注:举个例子, (-> a-map :key)
会展开成 (:key a-map)
,而不是 :key a-map
。]
如果有多于一个形式,那么将第一个形式作为第二个项插入到第二个形式中,以此类推。
; 应用多个函数
user=> (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first)
"X"
user=> (use '[clojure.walk :only [macroexpand-all]])
nil
user=> (macroexpand-all '(-> "a b c d"
.toUpperCase
(.replace "A" "X")
(.split " ")
first))
(first (. (. (. "a b c d" toUpperCase) replace "A" "X") split " "))
; 从深层嵌套的 map 中取出值
user=> (def language {:clojure {:author {:first-name "Rich" :last-name "Hickey"}}})
#'user/language
user=> (:first-name (:author (:clojure language)))
"Rich"
user=> (-> language :clojure :author :first-name)
"Rich"
->>¶
将多个形式串连成一个表达式。
如果只有一个参数 x
和一个形式 form
,那么将 x
作为最后一项(item),插入到 form
当中,如果 form
不是一个列表,那么将 form
包裹到一个列表里面。[译注:举个例子, (->> a-map :key)
会展开成 (:key a-map)
,而不是 :key a-map
。]
如果有多于一个形式,那么将第一个形式作为最后一项插入到第二个形式中,以此类推。
; 计算小于 10 的整数的平方之和
user=> (->> (range)
(map #(* % %))
(filter even?)
(take 10)
(reduce +))
1140
user=> (use '[clojure.walk :only [macroexpand-all]])
nil
user=> (macroexpand-all '(->> (range)
(map #(* % %))
(filter even?)
(take 10)
(reduce +)))
(reduce + (take 10 (filter even? (map (fn* [p1__3#] (* p1__3# p1__3#)) (range)))))
->¶
将多个形式串连成一个表达式。
如果只有一个参数 x
和一个形式 form
,那么将 x
作为第二个项(item),插入到 form
当中,如果 form
不是一个列表,那么将 form
包裹到一个列表里面。[译注:举个例子, (-> a-map :key)
会展开成 (:key a-map)
,而不是 :key a-map
。]
如果有多于一个形式,那么将第一个形式作为第二个项插入到第二个形式中,以此类推。
; 应用多个函数
user=> (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first)
"X"
user=> (use '[clojure.walk :only [macroexpand-all]])
nil
user=> (macroexpand-all '(-> "a b c d"
.toUpperCase
(.replace "A" "X")
(.split " ")
first))
(first (. (. (. "a b c d" toUpperCase) replace "A" "X") split " "))
; 从深层嵌套的 map 中取出值
user=> (def language {:clojure {:author {:first-name "Rich" :last-name "Hickey"}}})
#'user/language
user=> (:first-name (:author (:clojure language)))
"Rich"
user=> (-> language :clojure :author :first-name)
"Rich"
->>¶
将多个形式串连成一个表达式。
如果只有一个参数 x
和一个形式 form
,那么将 x
作为最后一项(item),插入到 form
当中,如果 form
不是一个列表,那么将 form
包裹到一个列表里面。[译注:举个例子, (->> a-map :key)
会展开成 (:key a-map)
,而不是 :key a-map
。]
如果有多于一个形式,那么将第一个形式作为最后一项插入到第二个形式中,以此类推。
; 计算小于 10 的整数的平方之和
user=> (->> (range)
(map #(* % %))
(filter even?)
(take 10)
(reduce +))
1140
user=> (use '[clojure.walk :only [macroexpand-all]])
nil
user=> (macroexpand-all '(->> (range)
(map #(* % %))
(filter even?)
(take 10)
(reduce +)))
(reduce + (take 10 (filter even? (map (fn* [p1__3#] (* p1__3# p1__3#)) (range)))))
aclone¶
(aclone array)
返回Java数组的克隆,作用于已知类型的数组
user=> (def a (int-array [1 2 3 4]))
#'user/a
user=> (def b (aclone a))
#'user/b
user=> (aset b 0 23)
23
user=> (vec b)
[23 2 3 4]
user=> (vec a)
[1 2 3 4]
alength¶
(alength array)
返回Java数组长度,作用于所有类型
(def array (into-array Integer/TYPE [1 2 3 4 5]))
#'user/array
(alength array)
5
alter-var-root¶
(alter-var-root v f & args)
原子性地修改 var v
的值,新值由 v
的旧值和给定的 args
应用到函数 f
得出。
user=> (def v 10)
#'user/v
user=> (alter-var-root (var v) + 1) ; (var v) 等同于 #'v
11
user=> v
11
assoc¶
assoc 表示 associate 的意思。
assoc
接受一个 Map ,还有一个或多个 key-val
对,
返回一个和传入 Map 类型相同的新 Map ,
除了原来传入 Map 已有的数据外,
新 Map 还包含传给 assoc
的那些 key-val
对。
当一个向量被应用到 assoc
函数时,
返回一个新向量,
新向量的索引下标(index) key
的值就是 val
。
注意索引下标必须 <= (count vector)
。
user=> (assoc {} :Clojure "Rich")
{:Clojure "Rich"}
user=> (assoc {:Clojure "Rich"} :Clojure "Rich Hickey") ; 如果有同名 key ,那么那么覆盖它
{:Clojure "Rich Hickey"}
user=> (assoc [1 2 3] 0 10086)
[10086 2 3]
user=> (assoc [1 2 3] 3 10086) ; key 最大可以等于 (count vector)
[1 2 3 10086]
user=> (assoc [1 2 3] 10086 10086) ; key 不能大于 (count vector)
IndexOutOfBoundsException clojure.lang.PersistentVector.assocN
(PersistentVector.java:136)
bigdec¶
(bigdec x)
将 x
强制转换为 BigDecimal
。
user=> (bigdec 3.0)
3.0M
user=> (bigdec 5)
5M
user=> (bigdec -1)
-1M
user=> (bigdec -1.0)
-1.0M
bigint¶
(bigint x)
将 x
强制转换为 BigInt
。
user=> (def x (biginteger 19931029))
#'user/x
user=> (class x)
java.math.BigInteger
biginteger¶
(biginteger x)
将 x
强制转换为 BigInteger
。
user=> (def x (biginteger 19931029))
#'user/x
user=> (class x)
java.math.BigInteger
comment¶
(comment & body)
忽略 body
,返回 nil
。
user=> (comment hello-clojure)
nil
user=> (comment "clojure!")
nil
user=> (defn msg []
(comment "nothing but a greeting message here")
(println "hello"))
#'user/msg
user=> (msg)
hello
nil
comp¶
comp
接受一系列函数作为输入,
返回一个匿名函数。
这个匿名函数接受可变数量的参数(variable number of args), 并按从右到左的顺序, 将传入的函数应用到参数中。
user=> ((comp str double +) 3 3 3) ; 以下两个表达等价
"9.0"
user=> (str (double (+ 3 3 3)))
"9.0"
compile¶
(compile lib)
编译符号(symbol) lib
定义的命名空间到一系列的class文件。 lib
的源文件必须在正确的类关联(classpath-relative)的目录下,输出的文class文件将会在 *compile-path*
定义的目录里,这个目录也必须在类路径(classpath)里面。
complement¶
(complement f)
接受一个函数 f
,返回一个匿名函数。
这个匿名函数接受的参数、产生的作用都和 f
一样,
但它返回的真值和 f
相反。
user=> (defn f []
(println "hello")
false)
#'user/f
user=> (f)
hello
false
user=> ((complement f))
hello
true
concat¶
返回一个惰性序列,序列里包含所有传入 collection 的全部元素。
; 另个、一个或多个 collection 组合
user=> (concat)
()
user=> (concat [1])
(1)
user=> (concat [1] [2])
(1 2)
user=> (concat [1] [2] [3])
(1 2 3)
user=> (concat [1] [2] [3] [4 5 6])
(1 2 3 4 5 6)
; 传入 concat 的参数必须都是 collection
; 组合元素和 collection 是 cons 和 conj 的任务
; user=> (concat 1 [2 3])
; IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:487)
cond¶
(cond & clauses)
接受一系列 test
/expression
对,
它每次对一个 test
进行求值,
如果某个 test
返回 true
,
那么 cond
求值并返回与这个 test
相对应的 expression
,
并且不再对其他 test
进行求值。
(cond)
返回 nil
。
user=> (defn type-of-number [n]
(cond (> n 0) "positive number"
(< n 0) "negative number"
:else "zero"))
#'user/type-of-number
user=> (type-of-number 10)
"positive number"
user=> (type-of-number -5)
"negative number"
user=> (type-of-number 0)
"zero"
conj¶
conj
的完整词义是 conjoin ,
表示『相连接』的意思,
它用于将元素和 collection 拼接起来。
需要注意的是,
根据 coll
的类型,
组合会发生在 coll
的不同地方,
也即是, 元素 x
可能会被加入到 coll
的最左边,也可能会被加入到最右边。
当 coll
等于 nil
,
也即是,执行 (conj nil item)
时,
结果为 (item)
。
; coll 为 nil
user=> (conj nil 1)
(1)
; 向量的组合在尾部进行
user=> (conj [0 1 2] 3)
[0 1 2 3]
; 列表的组合在头部进行
user=> (conj (list 0 1 2) 3)
(3 0 1 2)
; 处理多个元素的 conj
; 注意向量和列表的结果之间的不同
user=> (conj [0 1 2] 3 4 5)
[0 1 2 3 4 5]
user=> (conj (list 0 1 2) 3 4 5)
(5 4 3 0 1 2)
cons¶
(cons x seq)
返回一个新的序列,
序列的第一个元素是 x
,
而 seq
则是序列的其余部分。
; cons 起数字 1 和空列表
user=> (cons 1 '())
(1)
; cons 起数字 1 和列表 (2 3)
user=> (cons 1 (list 2 3))
(1 2 3)
constantly¶
(constantly x)
返回一个匿名函数,
接受任何数量的参数,
但总是返回 x
。
user=> (def ten (constantly 10))
#'user/ten
user=> (ten)
10
user=> (ten 1)
10
user=> (ten 1 2)
10
user=> (ten 1 2 3)
10
contains?¶
(contains? coll key)
如果 key
存在于给定 coll
中,
那么返回 true
,否则返回 false
。
对于那些使用数值索引(index)的 collection 、比如向量和 Java 数组来说,
contains?
用于测试给定的数值 key
是否在索引的范围(range)之内。
contains?
不是线性复杂度的操作,
它可以在常数或对数复杂度内完成。
如果要检查一个 coll
是否符合某个条件,可以使用 some 函数。
user=> (contains? {:clojure "Rich"} :python) ; 测试 Map
false
user=> (contains? {:clojure "Rich"} :clojure)
true
user=> (contains? [1 3 5 7 9] 3) ; 测试向量
true
user=> (contains? [1 3 5 7 9] 10086)
false
count¶
(count coll)
返回 coll
中元素的数量。
(count nil)
返回 0
。
coll
也可以是字符串、数组、Java Collection 和 Map 。
user=> (count nil)
0
user=> (count [1 2 3 4])
4
user=> (count (list 1 2 3 4))
4
user=> (count "string")
6
user=> (count {:clojure "Rich" :python "Guido" :ruby :Matz})
3
counted?¶
(counted? coll)
如果 coll
实现了常数复杂度的 count
操作,那么返回 true
。
; 向量、列表、Map 和集合的 count 操作都是常数复杂度的
; 但字符串不是
user=> (counted? [1 2 3])
true
user=> (counted? '(1 2 3))
true
user=> (counted? {:clojure "Rich"})
true
user=> (counted? #{:a :b :c})
true
user=> (counted? "string")
false
declare¶
(declare & names)
定义一些无绑定的 var 名字,用于提前声明(forward declarations)。
user=> (defn f []
(g))
;CompilerException java.lang.RuntimeException: Unable to resolve symbol: g in this context, compiling:(NO_SOURCE_PATH:2)
user=> (declare g)
#'user/g
user=> (defn f []
(g))
#'user/f
defn-¶
(defn- name & decls)
作用和 defn
类似,唯一的不同是创建的函数是私有的。
user=> (defn- msg [] "hello moto")
#'user/msg
user=> (msg)
"hello moto"
user=> (meta #'msg)
{:arglists ([]), :ns #<Namespace user>, :name msg, :private true, :line 1, :file "NO_SOURCE_PATH"}
defonce¶
(defonce name expr)
将 name
的 root value 设置为 expr
的值,当且仅当 name
还没有设置 root value 。
如果 name
已经有 root value ,那么 expr
不会被求值。
user=> number ; 没有 root value
;CompilerException java.lang.RuntimeException: Unable to resolve symbol: number in this context, compiling:(NO_SOURCE_PATH:0)
user=> (defonce number 10086) ; 设置 root value
#'user/number
user=> number
10086
user=> (defonce number 123123) ; 已有 root value ,设置失败
nil
user=> number
10086
defprotocol¶
(defprotocol name & opts+sigs)
协议是一组命名方法和签名
(defprotocol IOFactory
(make-reader [this] "Create a Buffered Reader")
(make-writer [this] "Create a Buffered Writer")
)
delay¶
(delay & body)
参数 body
接受一系列表达式,并返回一个 Delay
对象。
这个 Delay
对象只有在第一次被 force
函数、 deref
函数或者 @
宏强迫求值时,才会对 body
进行求值。
body
的求值结果会被缓存,之后对这个 Delay
对象的所有强迫求值,都返回这个缓存结果。
user=> (def d (delay (println "force delay object")
(+ 1 1)))
#'user/d
user=> d
#<Delay@15c5bba: :pending>
user=> @d
force delay object ; 第一次强迫求值
2 ; 这个值会被缓存
user=> @d
2 ; 不再求值 body ,只返回缓存值
user=> d
#<Delay@15c5bba: 2> ; 打印值现在带有 body 的值
delay?¶
(delay? x)
如果 x
是用 delay
创建的一个 Delay
对象,那么返回 true
。
user=> (delay? 2)
false
user=> (delay? (delay))
true
deref¶
deref
等同于这些读入器宏(reader macro): @ref
/ @agent
/ @var
/ @atom
/ @delay
/ @future
/ @promise
。
在事务中调用 deref
时,返回 ref
的事务值(in-transaction-value);在非事务情况下调用,则返回 ref
的最近一次提交值(most-recently-committed value).
应用于 var
、 agent
或 atom
时,返回它们的当前状态。
应用于 future
时,如果计算尚未完成,那么阻塞。
应用于 promise
时,如果该 promise
还没用 deliver
设置过值,那么阻塞。
带 timeout
参数的变种(variant)用于处理 future
和 promise
这种可能会阻塞的引用(reference):当阻塞时长超过 timeout
毫秒,而引用还未有值可用时,返回 timeout-val
作为值。
; 普通的 deref
user=> (def d (delay (+ 1 1)))
#'user/d
user=> (deref d)
2
user=> @d
2
; 带超时的 deref
user=> (def p (promise))
#'user/p
user=> (deref p 5000 nil) ; 5 秒内没有可用值,就返回 nil
nil
dissoc¶
dissoc 表示 dissociate 的意思。
dissoc
接受一个 Map ,以及任意个数的 key
,
返回一个和传入 Map 类型相同的新 Map ,
这个新 Map 不包含所有给定 key
的映射。
user=> (dissoc {:clojure "Rich" :python "Guido"}) ; 没有传入 key
{:python "Guido", :clojure "Rich"}
user=> (dissoc {:clojure "Rich" :python "Guido"} :python) ; 传入单个 key
{:clojure "Rich"}
user=> (dissoc {:clojure "Rich" :python "Guido"} :ruby) ; 传入一个不存在于 Map 的 key
{:python "Guido", :clojure "Rich"}
user=> (dissoc {:clojure "Rich" :python "Guido" :ruby "Matz"} :python :ruby) ; 传入多个 key
{:clojure "Rich"}
distinct¶
(distinct coll)
给定一个 coll
,返回一个无重复元素的惰性序列。
; 有重复元素的向量
user=> (distinct [1 2 1 2])
(1 2)
; 无重复元素的向量
user=> (distinct [1 2 3 4])
(1 2 3 4)
doall¶
对于那些带副作用的函数所生成的惰性序列来说,只有当列表的某个元素被求值时,该元素的副作用才会被显现出来。
doall
使用 next
函数遍历整个序列,从而迫使惰性序列的副作用产生。
这个函数返回序列的首个元素作为结果,因此它需要在内存中保存整个序列。
自 Clojure 1.0 以来可用。
user=> (def one-to-three (range 3))
#'user/one-to-three
user=> (doall one-to-three)
(0 1 2)
user=> one-to-three
(0 1 2)
dorun¶
对于那些带副作用的函数所生成的惰性序列来说,只有当列表的某个元素被求值时,该元素的副作用才会被显现出来。
dorun
使用 next
函数遍历整个序列,从而迫使惰性列表的副作用产生。
这个函数返回 nil
,而不是序列的首个元素。
自 Clojure 1.0 版本以来可用。
user=> (def infinity-hi (repeatedly #(println "hi")))
#'user/infinity-hi
user=> (dorun 5 infinity-hi)
hi
hi
hi
hi
hi
hi
nil
user=> (def one-to-ten (map println (range 10)))
#'user/one-to-ten
user=> (dorun one-to-ten)
0
1
2
3
4
5
6
7
8
9
nil
doseq¶
使用和 for
宏一样的绑定和过滤器,反复执行 body
(通常是为了产生副作用)。
返回 nil
而不是序列的首元素作为函数结果。
自 Clojure 1.0 版本以来可用。
user=> (doseq [i (range 10)] (prn i))
0
1
2
3
4
5
6
7
8
9
nil
empty?¶
(empty? coll)
如果 coll
中不包含任何元素,那么返回 true
,
效果等同于执行 (not (seq coll))
。
请使用惯用法 (seq x)
代替 (not (empty? x))
。
user=> (empty? '())
true
user=> (empty? nil)
true
user=> (empty? [1 2 3])
false
first¶
(first coll)
返回 coll
中的第一个元素。
传入的 coll
会被 seq
函数处理。
如果 coll
为 nil
,返回 nil
。
user=> (first nil)
nil
user=> (first [1 2 3])
1
user=> (first (list 1 2 3))
1
user=> (first {:clojure "Rich" :python "Guido" :ruby "Matz"})
[:python "Guido"]
file-seq¶
(file-seq dir)
返回一个惰性序列,
序列包含给定目录 dir
的整个目录树
(包括目录中的文件和目录中的文件夹及文件夹里的文件)。
dir
必须是一个 java.io.File
对象。
; 引入 file 函数,它可以根据路径名创建一个 File 对象
; 我们打开 /tmp 文件夹,并打印它的目录树
user=> (use '[clojure.java.io :only [file]])
nil
user=> (def tmp-folder (file "/tmp"))
#'user/tmp-folder
user=> (file-seq tmp-folder)
(#<File /tmp> #<File /tmp/.esd-1000> #<File /tmp/.esd-1000/socket> #<File /tmp/.Test-unix> #<File /tmp/mongodb-27017.sock> #<File /tmp/at-spi2> #<File /tmp/at-spi2/socket-1179-1131176229> #<File /tmp/at-spi2/socket-1268-1804289383> #<File /tmp/at-spi2/socket-1169-1369485920> #<File /tmp/mongodb-28017.sock> #<File /tmp/.X0-lock> #<File /tmp/.org.chromium.Chromium.NUsJHg> #<File /tmp/.org.chromium.Chromium.NUsJHg/SingletonSocket> #<File /tmp/.org.chromium.Chromium.NUsJHg/SingletonCookie> #<File /tmp/keyring-NwNaja> #<File /tmp/keyring-NwNaja/ssh> #<File /tmp/keyring-NwNaja/gpg> #<File /tmp/keyring-NwNaja/pkcs11> #<File /tmp/keyring-NwNaja/control> #<File /tmp/.ICE-unix> #<File /tmp/.ICE-unix/1303> #<File /tmp/cron.qpBNVU> #<File /tmp/pulse-PKdhtXMmr18n> #<File /tmp/ssh-ElvUhBgb1303> #<File /tmp/ssh-ElvUhBgb1303/agent.1303> #<File /tmp/.font-unix> #<File /tmp/pulse-397VI5uG1yhc> #<File /tmp/pulse-397VI5uG1yhc/pid> #<File /tmp/pulse-397VI5uG1yhc/native> #<File /tmp/pulse-397VI5uG1yhc/dbus-socket> #<File /tmp/hsperfdata_huangz> #<File /tmp/hsperfdata_huangz/9350> #<File /tmp/.X11-unix> #<File /tmp/.X11-unix/X0> #<File /tmp/.XIM-unix> #<File /tmp/.esd-120> #<File /tmp/pulse-T9RwKSB1FebW>)
; 使用 doseq 、 sort 和 println 函数
; 打印一个更美观的、经过排序的目录树
user=> (doseq [f (sort (file-seq tmp))]
(println f))
#<File /tmp>
#<File /tmp/.ICE-unix>
#<File /tmp/.ICE-unix/1303>
#<File /tmp/.Test-unix>
#<File /tmp/.X0-lock>
#<File /tmp/.X11-unix>
#<File /tmp/.X11-unix/X0>
#<File /tmp/.XIM-unix>
#<File /tmp/.esd-1000>
#<File /tmp/.esd-1000/socket>
#<File /tmp/.esd-120>
#<File /tmp/.font-unix>
#<File /tmp/.org.chromium.Chromium.NUsJHg>
#<File /tmp/.org.chromium.Chromium.NUsJHg/SingletonCookie>
#<File /tmp/.org.chromium.Chromium.NUsJHg/SingletonSocket>
#<File /tmp/at-spi2>
#<File /tmp/at-spi2/socket-1169-1369485920>
#<File /tmp/at-spi2/socket-1179-1131176229>
#<File /tmp/at-spi2/socket-1268-1804289383>
#<File /tmp/cron.qpBNVU>
#<File /tmp/hsperfdata_huangz>
#<File /tmp/hsperfdata_huangz/9350>
#<File /tmp/keyring-NwNaja>
#<File /tmp/keyring-NwNaja/control>
#<File /tmp/keyring-NwNaja/gpg>
#<File /tmp/keyring-NwNaja/pkcs11>
#<File /tmp/keyring-NwNaja/ssh>
#<File /tmp/mongodb-27017.sock>
#<File /tmp/mongodb-28017.sock>
#<File /tmp/pulse-397VI5uG1yhc>
#<File /tmp/pulse-397VI5uG1yhc/dbus-socket>
#<File /tmp/pulse-397VI5uG1yhc/native>
#<File /tmp/pulse-397VI5uG1yhc/pid>
#<File /tmp/pulse-PKdhtXMmr18n>
#<File /tmp/pulse-T9RwKSB1FebW>
#<File /tmp/ssh-ElvUhBgb1303>
#<File /tmp/ssh-ElvUhBgb1303/agent.1303>
nil
filter¶
(filter pred coll)
返回一个惰性序列,
序列中包含 coll
里所有 (pred item)
测试结果为 true
的项。
pred
必须是一个无副作用的函数。
; 过滤 0 - 9 中所有的奇数
user=> (filter even? (range 10))
(0 2 4 6 8)
; 过滤 0 - 9 中所有的偶数
user=> (filter odd? (range 10))
(1 3 5 7 9)
; 过滤 0 - 9 中所有小于 10086 的数,结果为空
user=> (filter #(> % 10086) (range 10))
()
fn¶
定义一个(匿名)函数。
user=> (fn greeting [name] ; 创建匿名函数
(str "Hello, " name " ."))
;#<user$eval1$greeting__2 user$eval1$greeting__2@616fde>
user=> ((fn greeting [name] ; 应用匿名函数
(str "hello, " name " ."))
"moto")
"hello, moto ."
user=> ((fn greeting ; 参数重载(arity overloading)
([name]
(greeting "Hello" name))
([msg name]
(str msg ", " name " .")))
"moto")
"Hello, moto ."
user=> ((fn greeting ; 接受不定数量参数的函数
[name & others]
(if (seq others)
(str "Hello, " name " and others: " others " .")
(str "Hello, " name " .")))
"moto" "nokia" "apple")
"Hello, moto and others: (\"nokia\" \"apple\") ."
future¶
(future & body)
参数 body
接受一系列表达式,并返回一个 future
对象,使用 deref
或者 @
可以对这个对象进行强迫求值。
future
对象会在另一个线程里对 body
进行求值,并将求值结果保存到缓存中,之后对这个 future
对象的所有强迫求值都会返回这个缓存值。
如果强迫求值时 body
的计算还没完成,那么调用者将被阻塞,直到计算完成,或者 deref
设置的过期时间到达为止。
; 普通的 future 调用
user=> (def f (future (+ 1 1)))
#'user/f
user=> f
#<core$future_call$reify__6110@fae040: 2>
user=> @f
2
; 一个停滞 5 秒的 future 调用
user=> @(future (println "start sleep") (Thread/sleep 5000) 10086)
start sleep
10086
; 一个带 timeout 的 deref 调用的例子,防止阻塞时间过长
user=> (deref (future (Thread/sleep 10000000000000000))
1000
"reach block timeout") ; 停滞 1 秒之后返回字符串值
"reach block timeout"
get¶
返回 map
中 key
所映射的值。
如果该 key
不存在,且给定了 not-found
,那么返回 not-found
,否则,返回 nil
。
user=> (def clojure {:author "Rich Hickey"})
#'user/clojure
user=> (get clojure :author)
"Rich Hickey"
user=> (get clojure :version) ; 无 not-found 参数
nil
user=> (get clojure :version 1.4) ; 有 not-found 参数
1.4
if-let¶
如果 test
为真,那么结合 binding-form
绑定,对 then
部分进行求值。
如果 test
为假,那么对 else
部分进行求值。
user=> (defn sum-all-even-number [all-number]
(if-let [all-even-number (filter even? all-number)]
(reduce + all-even-number)
0))
#'user/sum-all-even-number
user=> (sum-all-even-number [1 2 3 4 5 6 7 8 9])
20 ; 2 + 4 + 6 + 8
user=> (sum-all-even-number [1 3 5 7 9])
0
if-not¶
对 test
部分进行求值,如果结果为 false
,那么对 then
部分求值。
另一方面,如果 test
部分的求值结果为 true
,且 else
部分不为空,那么求值 else
部分;否则返回 nil
。
user=> (if-not false :then-part :else-part)
:then-part
user=> (if-not true :then-part)
nil
user=> (if-not true :then-part :else-part)
:else-part
import¶
(import & import-symbols-or-lists)
import-list => (package-symbol class-name-symbols*)
对于 class-name-symbols
中的每个 name
来说,
将名字为 package.name
的类添加到当前 namespace 当中。
可以在 ns
宏中通过 :import
来调用这个函数。
user=> (import java.util.Date) ; 载入单个类
java.util.Date
user=> (str (Date.))
"Wed Jun 20 23:18:42 CST 2012"
user=> (import '(java.util Date Calendar) ; 载入多个类
'(java.net URI ServerSocket))
java.net.ServerSocket
user=> (ns foo.bar ; 在 ns 宏中使用
(:import (java.util Date Calendar)
(java.net URI ServerSocket)))
java.net.ServerSocket
into¶
(into to from)
将from-coll中的所有元素合并至to-coll并返回结果
user=> (into (sorted-map) [ [:a 1] [:c 3] [:b 2] ] )
{:a 1, :b 2, :c 3}
user=> (into (sorted-map) [ {:a 1} {:c 3} {:b 2} ] )
{:a 1, :b 2, :c 3}
iterate¶
返回一个惰性序列,
序列元素的值为 x
、 (f x)
、 (f (f x))
、 (f (f (f x)))
,
诸如此类。
函数 f
必须是无副作用的。
; 生成一个计算所有正整数的惰性序列
user=> (def z (iterate inc 1))
#'user/z
; 取出第一个和第二个正整数
user=> (nth z 0)
1
user=> (nth z 1)
2
; 取出前十个正整数
user=> (take 10 z)
(1 2 3 4 5 6 7 8 9 10)
keep-indexed¶
(keep-indexed f coll)
对于 coll
中的每个项 item
,
以及 item
对应的索引下标 index
,
(keep-indexed f coll)
返回一个惰性序列,
序列中包含 (f index item)
除 nil
之外的所有计算结果。
因为带副作用的函数会返回与计算结果无关的虚假值,
因此,为了确保虚假值不会混进 keep-indexed
所生成的惰性序列中,
f
必须是一个无副作用的函数。
; 返回 0 - 9 内所有排序位置(index)为偶数的数字
user=> (keep-indexed #(if (even? %1) %2 nil) (range 10))
(0 2 4 6 8)
keep¶
(keep f coll)
对于 coll
中的每个项 item
,
(keep f coll)
返回一个惰性序列,
序列包含 (f item)
除 nil
之外的所有计算结果。
因为带副作用的函数会返回与计算结果无关的虚假值,
因此,为了确保虚假值不会混进 keep
所生成的惰性序列中,
f
必须是一个无副作用的函数。
user=> (keep inc [1 2 3])
(2 3 4)
; 将空的 collection 传给 seq 函数会返回 nil
; 可以根据这个性质来测试 keep
; 看它是否真的会省略等于 nil 的值
user=> (seq [])
nil
user=> (keep seq (list [1 2 3] [] [4 5 6]))
((1 2 3) (4 5 6))
keys¶
(keys map)
返回一个序列,序列里包含给定 map
的所有键(key)。
; 空 Map
user=> (keys {})
nil
; 非空 Map
user=> (keys {:python "Guido" :clojure "Rich" :ruby "Matz"})
(:python :ruby :clojure)
letfn¶
使用一个函数体 body
,以及一个带有函数规格(specs)的向量 fnspecs
,
将向量里的名字和相应的函数进行绑定。
向量里的名字在函数定义中,还有函数体内,都是可见的。
user=> (letfn [(twice [x]
(* x 2))
(six-times [y]
(* 3 (twice y)))]
(println "Twice 15 = " (twice 15))
(println "Six times 15 = " (six-times 15)))
Twice 15 = 30
Six times 15 = 90
nil
;; 名字 twice 和 six-times 在离开 letfn 之后不可用
;user=> (twice 15)
;CompilerException java.lang.RuntimeException: Unable to resolve symbol: twice in this context, compiling:(NO_SOURCE_PATH:7)
;user=> (six-times 15)
;CompilerException java.lang.RuntimeException: Unable to resolve symbol: six-times in this context, compiling:(NO_SOURCE_PATH:8)
line-seq¶
(line-seq rdr)
返回一个惰性序列,
序列里包含从文件 rdr
中读出的所有字符串行。
rdr
必须是一个 java.io.BufferedReader
对象。
; 引入 reader 函数,它可以创建一个 java.io.BufferedReader 对象
; 读出 animal.txt 文件中的所有内容,之后再将文件联接关闭
user=> (use '[clojure.java.io :only [reader]])
nil
user=> (def animal (reader "animal.txt"))
#'user/animal
user=> (line-seq animal)
("dog" "cat" "monkey" "lion" "tiger" "dolphin")
user=> (.close animal)
nil
; 用 with-open 来自动处理文件的打开和关闭
; 并用更美观的方式打印 animal.txt 文件的内容
user=> (with-open [animal (reader "animal.txt")]
(let [all-animal (line-seq animal)]
(doseq [a all-animal]
(println a))))
dog
cat
monkey
lion
tiger
dolphin
nil
map-indexed¶
返回一个惰性序列,
序列里的第一个元素是将 0
和 coll
的第一个元素应用到 f
所得出的值,
序列里的第二个元素是将 1
和 coll
的第二个元素应用到 f
所得出的值。。。
以此类推,直到 coll
被处理完为止。
函数 f
应该接受两个参数:一个索引值,一个是 coll
的元素值。
user=> (map-indexed (fn [idx item] [idx item]) "foobar")
([0 \f] [1 \o] [2 \o] [3 \b] [4 \a] [5 \r])
user=> (map-indexed vector "foobar") ; 另一种更简单的解法
([0 \f] [1 \o] [2 \o] [3 \b] [4 \a] [5 \r])
map¶
返回一个惰性序列,
序列的第一个元素是所有给定 collection
的第一个元素应用于函数 f
所得出的返回值,
序列的第二个元素是所有给定 collection
的第二个元素应用于函数 f
所得出的返回值。。。以此类推,一直到给定 collection
的其中一个被处理完为止。
当其中一个 collection
被处理完之后,其他 collection
的剩余元素会被忽略,因此结果序列的长度由给定 collection
中长度最短的那个决定。
函数 f
的参数个数应该和给定 collection
的数量一致。
;; 处理单个 collection
user=> (range 10)
(0 1 2 3 4 5 6 7 8 9)
user=> (map inc (range 10))
(1 2 3 4 5 6 7 8 9 10)
;; 处理两个 collection
user=> (range 0 10)
(0 1 2 3 4 5 6 7 8 9)
user=> (reverse (range 0 10))
(9 8 7 6 5 4 3 2 1 0)
user=> (map #(+ %1 %2) (range 10) (reverse (range 10)))
(9 9 9 9 9 9 9 9 9 9)
;; 处理长度不同的两个 collection
user=> (map #(+ %1 %2) (range 10086) (reverse (range 10)))
(9 9 9 9 9 9 9 9 9 9)
mapcat¶
(mapcat f & colls)
等同于调用 (concat (map f colls))
。
user=> (mapcat reverse [[3 2 1 0]
[6 5 4]
[9 8 7]])
(0 1 2 3 4 5 6 7 8 9)
max-key¶
将函数 f
应用到所有给定元素上,其中 (f item)
值最大的那个 item
被返回。
(f item)
的结果必须是数字值。
user=> (max-key count "abc"
"abcd"
"a"
"abcdefg"
"aa")
"abcdefg"
merge¶
(merge & maps)
返回一个含有剩余项的map。 如果key在多个map中存在,后面的值(从左向右的顺序)会覆盖前面的值
- ::
user=> (merge {:a 1 :b 2 :c 3} {:b 9 :d 4}) {:d 4, :a 1, :b 9, :c 3}
user=> (merge {:a 1 :b 2 :c 3} {:b 9 :d 4} {:b 8 :d 3}) {:d 3, :c 3, :b 8, :a 1}
min-key¶
将函数 f
应用到所有给定元素上,其中 (f item)
值最小的那个 item
被返回。
(f item)
的结果必须是数字值。
user=> (min-key count "aaaaaaa"
"bbbbbb"
"ccccccc"
"aa"
"dddddddd")
"aa"
next¶
(next coll)
返回 coll
除了第一个元素之外,余下的其他全部元素。
传入的 coll
会被 seq
函数处理。
如果 coll
除了 (first coll)
之外,
没有其他别的元素,那么返回 nil
。
user=> (next nil)
nil
user=> (next [1])
nil
user=> (next [1 2])
(2)
user=> (next [1 2 3])
(2 3)
partial¶
partial
接受一个函数 f
,
以及少于正常 f
所接受参数数量的参数,
并返回一个匿名函数。
当这个匿名函数被调用时,
传给它的附加参数(additional args)会和之前给定的参数一起,
传给函数 f
。
user=> (def three-times (partial * 3))
#'user/three-times
user=> (three-times 10) ; (* 3 10)
30
user=> (three-times 20) ; (* 3 20)
60
user=> (defn add-x-y-z [x y z]
(+ x y z))
#'user/add-x-y-z
user=> (def add-y-z (partial add-x-y-z 0)) ; x = 0
#'user/add-y-z
user=> (def add-z (partial add-y-z 1)) ; y = 1
#'user/add-z
user=> (add-z 2) ; z = 2
3 ; (+ 0 1 2)
pcalls¶
(pcalls & fns)
并行计算 fns
中的无参数函数,并以惰性序列的形式返回它们的值。
; 并行运行 3 个等待 3 秒的线程,共等待 3 秒
user=> (pcalls
#(Thread/sleep 3000)
#(Thread/sleep 3000)
#(Thread/sleep 3000))
(nil nil nil)
注解
因为 pcalls
和 pvalues
的返回值都是惰性序列,因此,如果有一个非常耗时的表达式阻塞在其他一些表达式的前面,那么就算后面的这些表达式已经计算完了,它们也不能被返回。
以下是这样一个实例,在序列前面的三个元素,可以立即被返回,但是,后面的三个元素只有等待 (Thread/sleep 3000)
执行完毕之后,才会被返回,尽管它们早就在并发线程里被求值完了:
user=> (for [i (pvalues 1 2 3
(Thread/sleep 3000)
(do (println "eval 4") 4)
(do (println "eval 5") 5)
(do (println "eval 6") 6))
]
(println i)
)
(1
2
nil eval 5 ; 从 println 的输出可以看到
eval 4 ; 4 、 5 、 6 三个数已经被计算出来了,但还没办法返回
3
nil eval 6
nil ; sleep 执行,停滞 3 秒
nil 4
nil 5
nil 6
nil nil)
pmap¶
pmap
类似于 map
,唯一的不同是, pmap
对函数 f
的应用是并行的。
pmap
的返回值是半惰性的(semi-lazy):
并行计算总是发生在消耗(consumption)之前,
不过,计算结果只有在被需要时,才会被 realize 。
只有当 f
为计算密集型函数,
而且并行获得的性能提升足以抵消并行所需的协调消耗时,
才应该使用 pmap
。
; 串行运行, 4 个元素,每个等待 3 秒,共等待 12 秒
user=> (time
(dorun
(map (fn [x] (Thread/sleep 3000))
(range 4))))
"Elapsed time: 12000.767484 msecs"
nil
; 并行运行, 4 个元素,每个等待 3 秒,共等待 3 秒
user=> (time
(dorun
(pmap (fn [x] (Thread/sleep 3000))
(range 4))))
"Elapsed time: 3002.602211 msecs"
nil
promise¶
(promise)
试验版本,将来可能会出现改动。
返回一个 promise
对象,可以使用 deref
或者 @
读取它的值,也可以使用 deliver
对它进行只能设置一次的赋值。
如果 promise
对象在使用 deliver
设置值之前,就被 deref
或者 @
读取,那么调用者将被阻塞,直到 promise
对象有值,或者 deref
设置的超时时间到期为止。
被 deliver
设置值之后,对 promise
的每次 deref
或者 @
都会不阻塞地返回 deliver
所设置的值。
user=> (def p (promise))
#'user/p
user=> p
#<core$promise$reify__6153@bfb588: :pending>
; 对未有值的 promise 进行 deref
; 为了避免陷入无限阻塞,设置 5 秒的超时时间
user=> (deref p 5000 "reach timeout")
"reach timeout"
; 为 promise 设置值
user=> (deliver p 10086)
#<core$promise$reify__6153@bfb588: 10086>
user=> @p
10086
user=> p
#<core$promise$reify__6153@bfb588: 10086>
; 试试重复 deliver ?
user=> (deliver p 123123)
nil
user=> @p
10086
pvalues¶
并行计算 exprs
中的表达式,并以惰性序列的形式返回它们的值。
user=> (pvalues
(Thread/sleep 3000)
10086
(Thread/sleep 3000)
"hello moto")
(nil 10086 nil "hello moto") ; 返回完整的计算结果需要 3 秒时间
注解
因为 pcalls
和 pvalues
的返回值都是惰性序列,因此,如果有一个非常耗时的表达式阻塞在其他一些表达式的前面,那么就算后面的这些表达式已经计算完了,它们也不能被返回。
以下是这样一个实例,在序列前面的三个元素,可以立即被返回,但是,后面的三个元素只有等待 (Thread/sleep 3000)
执行完毕之后,才会被返回,尽管它们早就在并发线程里被求值完了:
user=> (for [i (pvalues 1 2 3
(Thread/sleep 3000)
(do (println "eval 4") 4)
(do (println "eval 5") 5)
(do (println "eval 6") 6))
]
(println i)
)
(1
2
nil eval 5 ; 从 println 的输出可以看到
eval 4 ; 4 、 5 、 6 三个数已经被计算出来了,但还没办法返回
3
nil eval 6
nil ; sleep 执行,停滞 3 秒
nil 4
nil 5
nil 6
nil nil)
range¶
返回一个惰性序列,
序列里包含从大于等于 start
到小于 end
区间内的所有数字(start <= numbers < end
),
数字的步进以 step
指定。
默认情况下, start
为 0
, step
为 1
,而 end
则为无限。
; 不指定任何参数,返回一个包含所有非负整数的惰性序列
; 0, 1, 2, 3 ...
user=> (take 3 (range))
(0 1 2)
user=> (take 10 (range))
(0 1 2 3 4 5 6 7 8 9)
; 只指定 end
; 返回大于等于 0 到小于 end 之内的所有整数
user=> (range 5)
(0 1 2 3 4)
user=> (range 10)
(0 1 2 3 4 5 6 7 8 9)
; 指定 start 和 end
user=> (range 5 10)
(5 6 7 8 9)
user=> (range 0 10)
(0 1 2 3 4 5 6 7 8 9)
; 指定 start 、 end 和 step
; 第一个惰性序列计算 2 到 20 内的所有偶数
; 第二个惰性序列计算 1 到 20 内的所有奇数
user=> (range 2 20 2)
(2 4 6 8 10 12 14 16 18)
user=> (range 1 20 2)
(1 3 5 7 9 11 13 15 17 19)
re-seq¶
(re-seq re s)
返回一个惰性序列,
序列里包含字符串 s
中所有匹配模式 re
的值,
匹配使用 java.util.regex.Matcher.find()
进行,
每个匹配值都经过 re-groups
处理。
; 查找字符串中的所有数字值
user=> (re-seq #"[0-9]+" "abs123def345ghi567")
("123" "345" "567")
realized?¶
(realized? x)
如果给定的 promise
、 delay
、 future
或者 lazy-list
对象已经有值了,那么返回 true
。
user=> (def d (delay (+ 1 1)))
#'user/d
user=> (realized? d)
false
user=> @d
2
user=> (realized? d)
true
reduce¶
reduce
的行为由以下情况定义:
- 没有给定
val
: coll
为空:以无参数方式调用f
,调用所得的值为返回值。coll
只有一个元素:不调用f
,直接将那个元素用作返回值。coll
有多于一个元素:将coll
的前两个元素应用到f
,得到的结果再和coll
的第三个元素一起应用到f
,以此类推。
- 没有给定
- 有给定
val
: coll
为空:不调用f
,直接返回val
。coll
不为空:将val
和coll
的第一个元素应用到f
,得到的结果再和coll
的第二个元素一起应用到f
,以此类推。
- 有给定
f
应该是一个接受两个参数的函数,如果处理的 coll
可能为空,那么它还应该能进行无参数调用。
user=> (reduce + []) ; coll 为空, + 返回无参数调用结果 0
0
user=> (reduce + (range 10)) ; coll 不为空
45
user=> (reduce + 0 (range 10)) ; coll 不为空,且给定 val
45
reductions¶
返回一个惰性序列,
序列里包含计算 (reduce f coll)
所产生的所有中间结果。
如果给定了 init
,那么将它用作所有中间结果的初始值。
user=> (reduce + (range 1 10))
45
user=> (reductions + (range 1 10))
(1 3 6 10 15 21 28 36 45)
user=> (reductions + 0 (range 1 10)) ; 注意 init 不止添加到序列头那么简单
(0 1 3 6 10 15 21 28 36 45)
user=> (reductions + 10 (range 1 10)) ; 它还作为每个中间值的初始化值
(10 11 13 16 20 25 31 38 46 55)
remove¶
(remove pred coll)
返回一个惰性序列,
序列中包含 coll
里所有 (pred item)
测试结果为 false
的项。
pred
必须是一个无副作用的函数。
; 删除 0 - 9 中的所有偶数
user=> (remove even? (range 10))
(1 3 5 7 9)
; 删除 0 - 9 中的所有奇数
user=> (remove odd? (range 10))
(0 2 4 6 8)
; 删除 0 - 9 中所有大于等于 0 的数字,结果为空
user=> (remove #(>= % 0) (range 10))
()
repeat¶
返回一个包含 n
个 x
的惰性序列。
如果不指定 n
,那么值 x
可以被包含无限次。
; 定义一个包含 10 个 "hi" 的惰性序列
user=> (def ten-hi (repeat 10 "hi"))
#'user/ten-hi
user=> ten-hi
("hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi")
; 定义一个包含无限个 "hi" 的惰性序列
user=> (def infinite-hi (repeat "hi"))
#'user/infinite-hi
user=> (take 10 infinite-hi)
("hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi")
user=> (take 20 infinite-hi)
("hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi" "hi")
repeatedly¶
给定一个无参数的函数 f
(通常带有副作用),返回一个调用 f
函数 n
次的惰性序列。
如果不指定参数 n
,那么函数 f
可以执行无限次。
; 测试函数
user=> (defn greet []
"hi!")
#'user/greet
; 定义一个执行 10 次 greet 的惰性序列
; 并用 take 函数取出 5 个和 10 个 greet 的执行结果
user=> (def ten-greet (repeatedly 10 greet))
#'user/ten-greet
user=> (take 5 ten-greet)
("hi!" "hi!" "hi!" "hi!" "hi!")
user=> (take 10 ten-greet)
("hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!")
; 试图取出 10086 个值,但 ten-greet 最多只返回 10 个值
; 说明取出的数量最多只能和 n 一样大
user=> (take 10086 ten-greet)
("hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!")
; 定义一个执行无限次 greet 的惰性序列
user=> (def infinite-greet (repeatedly greet))
#'user/infinite-greet
user=> (take 10 infinite-greet)
("hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!")
user=> (take 100 infinite-greet)
("hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!" "hi!")
rseq¶
(rseq rev)
在常数时间内,返回 rev
的逆序序列。
rev
只能是向量或者 sorted-map 。
rev
为空时,返回 nil
。
; 空向量和空 sorted-map
user=> (rseq [])
nil
user=> (rseq (sorted-map))
nil
; 非空向量
user=> (rseq [1 2 3])
(3 2 1)
; 非空 sorted-map
user=> (def alpha (sorted-map :a "apple" :b "boy" :c "cat"))
#'user/alpha
user=> alpha
{:a "apple", :b "boy", :c "cat"}
user=> (rseq alpha)
([:c "cat"] [:b "boy"] [:a "apple"])
; 一些不能处理的情况
user=> (rseq (list 1 2 3))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Reversible clojure.core/rseq (core.clj:1480)
user=> (rseq nil)
NullPointerException clojure.core/rseq (core.clj:1480)
rsubseq¶
用法和 subseq 函数一样,但是返回的序列是逆序排序的。
等同于执行 (rseq (subseq sc test key))
或者 (rseq (subseq sc start-test start-key end-test end-key))
。
; 测试数据
user=> (def numbers (sorted-map 1 "one" 2 "two" 3 "three" 4 "four" 5 "five"))
#'user/numbers
user=> numbers
{1 "one", 2 "two", 3 "three", 4 "four", 5 "five"}
; 过滤所有键小于 1 大于 4 的键-值对,并逆序地返回结果
user=> (rsubseq numbers >= 2 <= 4)
([4 "four"] [3 "three"] [2 "two"])
; 过滤所有键小于 2 的键-值对,并逆序地返回结果
user=> (rsubseq numbers > 2)
([5 "five"] [4 "four"] [3 "three"])
seq¶
(seq coll)
根据给定的 coll
,返回一个相应的序列。
当 coll
为空时,返回 nil
。
(sql nil)
也返回 nil
。
seq
函数也可以作用于字符串、
(带有引用类型的)原生 Java 数组,
以及任何实现了 iterable
接口的对象。
; 处理空向量和 nil
user=> (seq [])
nil
user=> (seq nil)
nil
; 处理非空向量、列表、 Map 和字符串
user=> (seq [1 2 3])
(1 2 3)
user=> (seq (list 1 2 3))
(1 2 3)
user=> (seq {:language "clojure" :creator "Rich Hickey"})
([:creator "Rich Hickey"] [:language "clojure"])
user=> (seq "hello world")
(\h \e \l \l \o \space \w \o \r \l \d)
shuffle¶
(shuffle coll)
返回对 coll
进行乱序排列之后得出的序列。
user=> (shuffle [1 2 3 4])
[4 1 3 2]
user=> (shuffle [1 2 3 4])
[1 3 2 4]
some¶
(some pred coll)
如果 coll
中有一个元素 x
能满足 (pred x)
,则返回``(pred x)``的值
如果 coll
中没有任何元素 x
能满足 (pred x)
,
返回 nil
。
将一个集合用作 pred
是 some
的一个惯用法:
比如说,
(some #{:fred} coll)
在 :fred
存在于 coll
时返回 :fred
,
如果不存在,就返回 nil
。
user=> (some #{:fred} [:fred :peter :jack])
:fred
user=> (some #{:mary} [:fred :peter :jack])
nil
user=> (some #(>= % 10) [1 3 5 7 9]) ; 查看是否有 >= 10 的值存在
nil
user=> (some #(>= % 5) [1 3 5 7 9]) ; 查看是否有 >= 5 的值存在
true
sort¶
返回对 coll
进行排序之后得到的序列。
如果不指定 comparator
,
那么默认使用 compare.
,
comparator
必须实现 java.util.Comparator
。
user=> (sort [4 2 1 3])
(1 2 3 4)
user=> (sort >= [4 2 1 3])
(4 3 2 1)
user=> (sort <= [4 2 1 3])
(1 2 3 4)
subseq¶
过滤 sc
并返回一个序列,序列里的所有实体(entry)的键 ek
都必须符合条件 (true? (test (.. sc comparator (compare ek key)) 0))
。
如果没有任何实体符合条件,则返回 nil
。
参数 sc
必须是一个 sorted collection ,测试条件 test
可以是 <
、 <=
、 >
或者 >=
。
; 测试数据
user=> (def numbers (sorted-map 1 "one" 2 "two" 3 "three" 4 "four" 5 "five"))
#'user/numbers
user=> numbers
{1 "one", 2 "two", 3 "three", 4 "four", 5 "five"}
; 过滤所有键小于 3 的键-值对
user=> (subseq numbers >= 3)
([3 "three"] [4 "four"] [5 "five"])
; 过滤所有键小于 1 大于 4 的键-值对
user=> (subseq numbers >= 2 <= 4)
([2 "two"] [3 "three"] [4 "four"])
; 过滤所有键小于 10 的键-值对,返回 nil
user=> (subseq numbers >= 10)
nil
time¶
(time expr)
对 expr
进行求值,并打印求值所花费的时间。
expr
的值作为函数的返回值被返回。
; 计算对比两个关键字 100 次所需的时间
user=> (time
(dotimes [_ 100]
(= :kw :kw)))
"Elapsed time: 0.23802 msecs"
nil
tree-seq¶
(tree-seq branch? children root)
返回一个惰性序列, 序列里包含通过深度优先遍历得出的一棵树中的所有节点。
branch?
函数接受一个参数,
通过向它传入一个节点,可以判断该节点是否拥有子节点。
children
函数接受一个参数,
通过向它传入一个节点,可以得到一个包含该节点的所有子节点的序列。
children
函数只会在那些
branch?
函数返回 true
的节点被调用。
root
是树的根节点。
; 函数 file-seq 用于列出一个文件夹的整个目录树
; 它是展示 tree-seq 用法的一个极好的例子
(defn file-seq
[dir]
(tree-seq
(fn [^java.io.File f] (. f (isDirectory))) ; 检查文件 f 是不是一个文件夹
(fn [^java.io.File d] (seq (. d (listFiles)))) ; 如果是的话,就用 listFiles 方法遍历它
dir)) ; 树的根节点是传入的文件夹
vals¶
(vals map)
返回一个序列,序列里包含给定 map
的所有值(value)。
; 空 Map
user=> (vals {})
nil
; 非空 Map
user=> (vals {:python "Guido" :clojure "Rich" :ruby "Matz"})
("Guido" "Matz" "Rich")
when-let¶
如果 test
的值 value
为真,那么结合 binding-form
绑定,对 body
进行求值。
user=> (defn drop-one [coll]
(when-let [s (seq coll)]
(rest s)))
#'user/drop-one
user=> (drop-one [1 2 3])
(2 3)
when-not¶
(when-not test & body)
对 test
部分进行求值,如果结果为 false
,那么在一个隐式 do
的包围下对 body
进行求值。
user=> (when-not true
(println "hello")
(println "moto"))
nil
user=> (when-not false
(println "hello")
(println "moto"))
hello
moto
nil
with-open¶
(with-open bindings & body)
在绑定了名字 name
与初始值 init
的 try
表达式里面对 body
进行求值
并按照绑定时间从晚到早的顺序
在 finally
语句里面对每个 name
执行 (.close name)
调用
(ns io
(:require [clojure.java.io :as io]))
(defn readLineByLine [file-name]
(with-open [reader (io/reader file-name)]
(doseq [line (line-seq reader)]
(println line))))
(defn copyLineByLine [source target]
(with-open [reader (io/reader source)
writer (io/writer target)]
(io/copy reader writer)))
xml-seq¶
(xml-seq root)
返回一个惰性序列,序列里包含一棵 xml 元素树。
xml 文件可以用 clojure.xml/parse
函数解释。
; 解释一个 xml 文件,并提取内容
user=> (require 'clojure.xml)
nil
user=> (def content (clojure.xml/parse "http://www.w3schools.com/xml/note.xml"))
#'user/content
; 根据 xml 内容,生成 xml 树
user=> (def tree (xml-seq content))
#'user/tree
user=> tree
({:tag :note, :attrs nil, :content [{:tag :to, :attrs nil, :content ["Tove"]} {:tag :from, :attrs nil, :content ["Jani"]} {:tag :heading, :attrs nil, :content ["Reminder"]} {:tag :body, :attrs nil, :content ["Don't forget me this weekend!"]}]} {:tag :to, :attrs nil, :content ["Tove"]} "Tove" {:tag :from, :attrs nil, :content ["Jani"]} "Jani" {:tag :heading, :attrs nil, :content ["Reminder"]} "Reminder" {:tag :body, :attrs nil, :content ["Don't forget me this weekend!"]} "Don't forget me this weekend!")
; 遍历树
user=> (nth tree 0)
{:tag :note, :attrs nil, :content [{:tag :to, :attrs nil, :content ["Tove"]} {:tag :from, :attrs nil, :content ["Jani"]} {:tag :heading, :attrs nil, :content ["Reminder"]} {:tag :body, :attrs nil, :content ["Don't forget me this weekend!"]}]}
user=> (nth tree 1)
{:tag :to, :attrs nil, :content ["Tove"]}
user=> (nth tree 2)
"Tove"
user=> (nth tree 3)
{:tag :from, :attrs nil, :content ["Jani"]}
user=> (nth tree 4)
"Jani"
zipmap¶
(zipmap keys vals)
返回一个含有键和相应值的map
user=> (zipmap [:a :b :c :d :e] [1 2 3 4 5])
{:e 5, :d 4, :c 3, :b 2, :a 1}
clojure.core.async¶
buffer¶
(buffer n)
返回一个大小固定为n的buffer,当buffer满了的时候,put操作会阻塞
user=> (def c (chan (buffer 3)))
#'user/c
clojure.data¶
diff¶
(diff a b)
递归比较a和b,返回一个tuple 结构如 [只在a中出现的元素 只在b中出现的元素 a和b中共同出现的元素]
比较规则: * a和b相等 返回[nil,nil,a] * Map键值相同值不同视为不同 * Set无法比较差异 * 所有的序列都被视为索引的关联集合,结果以向量的类型返回 * 所有东西(包含string)都视为原子且平等比较
(use 'clojure.data)
(def uno {:same "same", :different "one"})
(def dos {:same "same", :different "two", :onlyhere "whatever"})
(diff uno dos)
=> ({:different "one"} {:onlyhere "whatever", :different "two"} {:same "same"})
;; {uno中出现的元素 } { dos中出现的元素 } {两者均有的元素}
(diff {:a 1} {:a 1 :b 2})
=> (nil {:b 2} {:a 1})
;; 并没有只在第一个集合中存在的元素
(diff [1 2 3] [5 9 3 2 3 7]) ;;=> [[1 2] [5 9 nil 2 3 7] [nil nil 3]]
(diff (set [1 2 3]) (set [5 9 3 2 3 7])) ;;=> [#{1} #{7 9 5} #{3 2}]
clojure.pprint¶
clojure.pprint 实现了一个灵活的工具,用于把clojure的数据结构输出成优雅易懂的格式。pretty printer最基本的使用方式,就是把代码中的 println 替换成 pprint。高级用户可以通过 the building blocks 来定制自己的输出格式。
pprint 对基础的数据提供了简单的格式,对 clojure source code 提供了专有的格式。其他更高级的格式,包括完全不像 clojure 惯用的数据格式的 XML ,JSON ,都可以通过自定义补丁来输出出来。
这个模块不仅包括 pprint, 也包括给 Common lisp 提供支持的 cl-format。因为 pprint 和 cl-format 需要共同作用,所以 pprint 支持非常简洁的自定义补丁。除此之外, pprint 也提供了 format function 作为 clojure 现有的标准 format function的一个替代。
欢迎查看 pprint 和 cl-format 的文档来获取更多的信息。
Added in Clojure version 1.2
cl-format¶
Common Lisp 兼容的格式输出的函数的实现。cl-format 的格式包括是输出到流里还是输出 成string都是通过不同的参数控制的。它支持极为复杂的机构化的数据。
Writer参数是java.io.Writer的一个实例,有的话输出成流,反之输出成String。format-in 用于格式控制。 args 指代数据将要被格式化输出的。
format control string 是通过格式化符号,标注怎么去把许多参数按不同的方式格式化的。 类似 String.format 的 dsl。
如果 writer 是控制,那么cl-format 返回 string,否则返回空值,输出到流里。
一个例子: (let [results [46 38 22]] (cl-format true “There ~[are~;is~:;are~]~:* ~d result~:p: ~{~d~^, ~}~%” (count results) results))
输出结果 out: There are 3 results: 46, 38, 22
详细的文档关于 format control string 在”Common Lisp the Language, 2nd edition”, Chapter 22 (available online at: http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000) and in the Common Lisp HyperSpec at http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm
1 EXAMPLE
;; 一种格式化integer的方式.
;; 第一个参数把输出定向到 *out*
user=> (cl-format true "~5d\n" 3)
3
nil
;; 第一个参数nil或false回直接输出成sting
user=> (cl-format nil "~5d" 3)
" 3"
user=> (cl-format nil "Pad with leading zeros ~5,'0d" 3)
"Pad with leading zeros 00003"
user=> (cl-format nil "Pad with leading asterisks ~5,'*d" 3)
"Pad with leading asterisks ****3"
;; 如果有办法去描述一个左对齐的数在一个 formatString 里请标示在这理。
;; 这个任务在我看来,可以首先去 formatted 数成 String 然后再使用 <width>
规则加到原来的输出结果上。
user=> (cl-format nil "~15a" (cl-format nil "~:d" 1234567))
"1,234,567 "
user=> (cl-format nil "Always print the sign ~5@d" 3)
"Always print the sign +3"
user=> (cl-format nil "Use comma group-separator every 3 digits ~12:d" 1234567)
"Use comma group-separator every 3 digits 1,234,567"
user=> (cl-format nil "decimal ~d binary ~b octal ~o hex ~x" 63 63 63 63)
"decimal 63 binary 111111 octal 77 hex 3f"
user=> (cl-format nil "base 7 ~7r with width and zero pad ~7,15,'0r" 63 63)
"base 7 120 with width and zero pad 000000000000120"
;; 在 cl-format 里不需要做任何转换 BigInt,
;; BigInteger, or BigDecimal.
user=> (cl-format nil "cl-format handle BigInts ~15d" 12345678901234567890)
"cl-format handles BigInts
12345678901234567890"
user=> (cl-format nil "Be aware of auto-conversion ~8,'0d ~8,'0d" 2.4 -5/4)
"Be aware of auto-conversion 000002.4
0000-5/4"
;; 下面看起来可能像是一个bug,但是是被 Common Lisp HyperSpec 写在文档上的一种方法,如果你觉得这样写不爽,
;; 你也可以这样写 (format "%08d" -2)
user=> (cl-format nil "~8,'0d" -2)
"000000-2"
clojure.string¶
blank?¶
(blank? s)
如果 s
是 nil
、空字符串 ""
或者只包含空白的字符串,那么返回 true
。
user=> (clojure.string/blank? nil)
true
user=> (clojure.string/blank? "")
true
user=> (clojure.string/blank? " ")
true
user=> (clojure.string/blank? "hello world")
false
escape¶
(escape s cmap)
使用函数 cmap
对字符串 s
中的每个字符 ch
进行转义,并返回一个新字符串。
转义按照以下规则进行:
- 如果
(cmap ch)
返回nil
,那么将ch
添加到新字符串 - 如果
(cmap ch)
不为nil
,那么将(str (cmap ch))
添加到新字符串。
user=> (clojure.string/escape "I want 1 < 2 as HTML, & other good things." {\< "<" \> ">" \& "&"})
"I want 1 < 2 as HTML, & other good things."
replace¶
(replace s match replacement)
将字符串 s
中的所有 match
实例(instance)替换成 replacement
。
match
/ replacement
可以是以下组合:
- 字符串 / 字符串
- 字符 / 字符
- 模式(pattern) / 字符串或一个函数,其中函数的参数为模式所匹配的实例
; 组合 1 :用字符串替换字符串
; 将字符串里的子串 moto 替换成 google
user=> (clojure.string/replace "hello moto" "moto" "google")
"hello google"
; 组合 2 :用字符替换字符
; 将字符串的所有小写 o 替换成大写 O
user=> (clojure.string/replace "hello moto" "o" "O")
"hellO mOtO"
; 组合 3 :用字符串替换匹配模式的实例
; 将匹配 #"red" 模式的实例替换为 "blue"
user=> (clojure.string/replace "The color is red" #"red" "blue")
"The color is blue"
; 另一个组合 3 :用给定函数的返回值来替换匹配模式的实例
; 将字符串里的所有原音字母转换为大写
user=> (clojure.string/replace "The color is red" #"[aeiou]" clojure.string/upper-case)
"ThE cOlOr Is rEd"
replace-first¶
(replace-first s match replacement)
和 replace 函数的作用类似,但只替换 match
的第一个实例。
; 只有第一个匹配的原音 e 被转为大写字母了
user=> (clojure.string/replace-first "The color is red" #"[aeiou]" clojure.string/upper-case)
"ThE color is red"
capitalize¶
(capitalize s)
将字符串 s
的首个字符转换成大写,剩余的其他字符全部转换为小写。
user=> (clojure.string/capitalize "hello world")
"Hello world"
user=> (clojure.string/capitalize "HELLO WORLD")
"Hello world"
lower-case¶
(lower-case s)
将字符串 s
的所有字符转换为小写。
user=> (clojure.string/lower-case "hello moto")
"hello moto"
user=> (clojure.string/lower-case "HELLO MOTO")
"hello moto"
upper-case¶
(upper-case s)
将字符串 s
的所有字符转换为大写。
user=> (clojure.string/upper-case "hello moto")
"HELLO MOTO"
user=> (clojure.string/upper-case "HELLO MOTO")
"HELLO MOTO"
join¶
先使用 (seq coll)
将 coll
转换为序列,然后返回一个包含序列里所有元素的字符串。
多个元素之间以一个可选的 separator
分隔开。
user=> (def fruit ["apple" "banana" "cherry"])
#'user/fruit
; 不使用 separator
user=> (clojure.string/join fruit)
"applebananacherry"
; 使用 ", " 作为 separator
user=> (clojure.string/join ", " fruit)
"apple, banana, cherry"
split¶
根据正则表达式 re
,对字符串 s
进行分割,结果所得的一个或多个字符串保存在一个向量里面。
可选的 limit
参数指定最大的分割次数。
这个函数不是惰性的。
; 分割 #"\r?\n" 是 clojure.string/split-lines 的定义
user=> (clojure.string/split "hello\nmoto\r\nagain\r\n" #"\r?\n")
["hello" "moto" "again"]
; 带 limit 参数
user=> (clojure.string/split "hello\nmoto\r\nagain\r\n" #"\r?\n" 1)
["hello\nmoto\r\nagain\r\n"]
user=> (clojure.string/split "hello\nmoto\r\nagain\r\n" #"\r?\n" 2)
["hello" "moto\r\nagain\r\n"]
user=> (clojure.string/split "hello\nmoto\r\nagain\r\n" #"\r?\n" 3)
["hello" "moto" "again\r\n"]
user=> (clojure.string/split "hello\nmoto\r\nagain\r\n" #"\r?\n" 4)
["hello" "moto" "again" ""]
user=> (clojure.string/split "hello\nmoto\r\nagain\r\n" #"\r?\n" 5)
["hello" "moto" "again" ""]
split-lines¶
(split-lines s)
在字符串 s
的 \n
或者 \r\n
处分割开。
user=> (clojure.string/split-lines "hello\nmoto\r\nagain\r\n")
["hello" "moto" "again"]
user=> (clojure.string/split-lines "no-new-lines")
["no-new-lines"]
user=> (clojure.string/split-lines "")
[""]
user=> (clojure.string/split-lines nil)
;NullPointerException java.util.regex.Matcher.getTextLength (Matcher.java:1234)
trim¶
从字符串的两端移除空白。
user=> (clojure.string/trim "clojure")
"clojure"
user=> (clojure.string/trim " clojure ")
"clojure"
trim-newline¶
(trim-newline s)
从字符串的末尾移除转行符 \n
和返回符 \r
。
类似于 Perl 的 chomp
函数。
user=> (clojure.string/trim-newline "test\n")
"test"
user=> (clojure.string/trim-newline "test\r")
"test"
user=> (clojure.string/trim-newline "test\n\r")
"test"
user=> (clojure.string/trim-newline "test\r\n")
"test"
; 只移除末尾的换行符
user=> (clojure.string/trim-newline "leading newline\n trailing newline\n")
"leading newline\n trailing newline"
clojure.set¶
difference¶
返回集合 s1
和其余给定集合之间的差。
当只有一个参数时,返回该参数。
user> (use 'clojure.set)
nil
; 单个集合
user> (difference #{:a :b :c})
#{:a :c :b}
user> (let [s #{:a :b :c}] (identical? s (difference s)))
true
; 两个集合
user> (difference #{:a :b :c} #{:a :b})
#{:c}
user> (difference #{:a :b :c} #{:a :b :c})
#{}
; 多个集合
user> (difference #{:a :b :c} #{:a} #{:b})
#{:c}
index¶
index
类似于 SQL 中的 group by
操作:它把 xrel
中的成员按 ks
中列举的 key
的值进行分组。
其中, xrel
和 ks
都是序列(sequence)。 xrel
的成员是一个个 map
, ks
的成员是用于分组的 key
。
index
返回一个新 map
,新 map
的 key
是 ks
的成员作为 key
在 xrel
中每个 map
取到的不同值,新 map
的 value
是满足这些值的 xrel
中成员的集合。
user> (use 'clojure.set)
nil
;; 处理 set
user> (def points #{{:x 0 :y 0} {:x 0 :y 1} {:x 1 :y 0}}) ;; 定义三个点
#'user/points
user> (index points [:x]) ;; group by x 坐标
{{:x 1} #{{:y 0, :x 1}},
{:x 0} #{{:y 1, :x 0} {:y 0, :x 0}}}
user> (index points [:y :x]) ;; group by x 和 y 坐标
{{:x 1, :y 0} #{{:y 0, :x 1}},
{:x 0, :y 0} #{{:y 0, :x 0}},
{:x 0, :y 1} #{{:y 1, :x 0}}}
user> (index points [:z]) ;; group by 不存在的 z 坐标
{{} #{{:y 1, :x 0} {:y 0, :x 0} {:y 0, :x 1}}}
;; 还可以处理 vector
user> (def points-vec [{:x 0 :y 0} {:x 0 :y 1} {:x 1 :y 0}])
#'user/points-vec
user> (index points-vec [:x])
{{:x 1} #{{:y 0, :x 1}}, {:x 0} #{{:y 1, :x 0} {:y 0, :x 0}}}
intersection¶
计算输入集合的交集。
user> (use 'clojure.set)
nil
user> (intersection #{1 2 3} #{3 4 5})
#{3}
user> (intersection #{1 2 3} #{2 3} #{3})
#{3}
join¶
join
类似于 SQL 中的 join
操作:它对 xrel
和 yrel
做关联操作。
xrel
和 yrel
是两个序列,序列的每个成员都是一个 map
, map
的每个 key-value 对可以看做数据库表的字段以及对应的值。
如果提供了 km
参数,则按照 km
所列出的 key
进行关联。
user> (use 'clojure.set)
nil
;; 处理 set
user> (def students ;; 学生信息
#{{:id 1 :name "Li Lei"}
{:id 2 :name "Han Meimei"}})
#'user/students
user> (def score ;; 学生成绩
#{{:id 1 :score 60}
{:id 2 :score 99}})
#'user/score
user> (join students score) ;; 关联信息和成绩
#{{:score 99, :name "Han Meimei", :id 2}
{:score 60, :name "Li Lei", :id 1}}
;; 处理 vector
user> (def score-vec
[{:id 1 :score 60}
{:id 2 :score 99}])
#'user/score
user> (join students score-vec)
#{{:score 99, :name "Han Meimei", :id 2}
{:score 60, :name "Li Lei", :id 1}}
map-invert¶
反转一个 map
,将它原本的 value
映射为新 map
的 key
,原本的 key
映射为新 map
的 value
。
注解
当多个 key
有同一个 value
时,新 map
只保留其中的一个作为 key
。
user> (use 'clojure.set)
nil
user> (map-invert {:a 1 :b 2})
{2 :b, 1 :a}
user> (map-invert {:a 1 :b 2 :c 2}) ;; 两个 2 冲突,丢掉了 :c
{2 :b, 1 :a}
project¶
对于 xrel
中的每个元素, project
的结果集合只包含那些 key
在 ks
里出现过的元素为成员。
换一种说法来讲就是:将 xrel
的成员投影到 ks
指定的维度上。
user> (use 'clojure.set)
nil
user> (def points ;; 定义三个三维空间的点
#{{:x 1 :y 0 :z 1}
{:x 0 :y 1 :z 1}
{:x 1 :y 1 :z 0}})
#'user/points
user> (project points [:x]) ;; 投影到 x 轴上
#{{:x 0} {:x 1}} ;; 返回值是一个集合,所以计算结果中的两个 {:x 1} 只有一个被保留
user> (project points [:x :y])
#{{:y 1, :x 0} {:y 1, :x 1} {:y 0, :x 1}} ;; 投影到 x-y 平面上
rename¶
把 xrel
中的元素的key改名,新旧名字的映射由 kmap
提供。
user> (use 'clojure.set)
nil
user> (def students
#{{:id 1 :name "Li Lei"}
{:id 2 :name "Han Meimei"}})
#'user/students
user> (rename students {:id :student-id})
#{{:name "Han Meimei", :student-id 2} {:name "Li Lei", :student-id 1}}
rename-keys¶
把 map
中的 key 按照 kmap
提供的映射改名。
可以使用 array-map
类型的 kmap
来指定替换执行的顺序。
注意,替换可能造成 key 冲突,导致原来的 key-value 对被覆盖。
user> (rename-keys {:id 1 :name "Li Lei"} {:id :new-id :name :new-name})
{:new-id 1, :new-name "Li Lei"}
; 改名造成 key 冲突,旧的 {:b 2} 被覆盖
user> (rename-keys {:a 1 :b 2} {:a :b})
{:b 1}
; 通过 array-map 指定替换执行的顺序
user> (rename-keys {:a 1 :b 2 :c 3} (array-map :a :tmp :b :a :tmp :b))
{:b 1, :a 2, :c 3}
select¶
返回 xset
中所有使 pred
为真的元素。
select
和 clojure.core/filter
类似,只是 select
的输入和输出都是 set
。
user=> (use 'clojure.set)
nil
user=> (select even? #{1 2 3 4 5})
#{2 4}
user=> (select even? [1 2 3 4 5]) ;; 只能是set
ClassCastException clojure.lang.PersistentVector cannot be cast to clojure.lang.IPersistentSet clojure.core/disj (core.clj:1420)
subset?¶
判断 set1
是否 set2
的子集。
user> (clojure.set/subset? #{:a :b} #{:a :b :c})
true
user> (clojure.set/subset? #{:a :b} #{:a :c :d})
false
user> (clojure.set/subset? #{} #{:a :c :d})
true
user> (clojure.set/subset? #{:a} #{:a})
true
superset?¶
判断 set1
是否 set2
的超集。
user> (clojure.set/superset? #{:a :b :c} #{:a :b})
true
user> (clojure.set/superset? #{:a :c :d} #{:a :b})
false
user> (clojure.set/superset? #{:a :b} #{:a :b})
true
user> (clojure.set/superset? #{:a :b} #{})
true
clojure.test¶
are¶
macro
通过以模板表达的方式检查多个断言(assertions
)。关于模板的解释请查阅 clojure.template/do-template
;例子:
(are [x y] (= x y)
2 (+ 1 1)
4 (* 2 2))
;宏将展开为:
(do (is (= 2 (+ 1 1)))
(is (= 4 (* 2 2))))
clojure.java.browse¶
clojure.java.io¶
Coercions¶
clojure.java.io
内部定义的protocol,有 as-file
和 as-url
两个方法,clojure为多种类型实现了这个protocol。
copy¶
把 input
的内容拷贝到 output
,成功返回 nil
,失败抛出 IOException
。
input
可以是 java.io.InputStream
, java.io.Reader
, java.io.File
, byte
数组,或者 java.lang.String
。当输入是 java.lang.String
的时候,是把字符串本身拷贝到输出。
output
可以是 java.io.OutputStream
, java.io.Writer
或者 java.io.File
。
opts
可以包含 :buffer-size
和 encoding
。 :buffer-size
默认1024。
除了自己打开的, copy
不会关闭任何流。
user> (use 'clojure.java.io)
nil
user> (copy "XXXXXX" (output-stream "/tmp/x"))
nil
user> (slurp "/tmp/x")
"XXXXXX"
user> (copy (file "/tmp/x") (output-stream "/tmp/xx"))
nil
user> (slurp "/tmp/x")
"XXXXXX"
IOFactory¶
clojure.java.io
内部定义的protocol,包含 make-reader
, make-writer
, make-input-stream
和 make-out-stream
四个方法。这四个方法创建的都是带缓冲区的reader,writer或stream。
用户应该避免直接使用上述四个API,而是使用 reader
, writer
, input-stream
和 output-stream
。
input-stream¶
根据 x
创建一个 java.io.BufferedInputStream
。
x
可以是 java.io.InputStream
, java.io.File
, java.net.URL
, java.net.URI
, java.lang.String
, java.net.Socket
, byte
数组或者 char
数组 。
当 x
是 java.lang.String
时,会先尝试把 x
解释成 java.net.URL
,如果失败,则是 java.io.File
。
opts
定义创建选项,key可以是 :append
和 :encoding
。
user> (use 'clojure.java.io)
nil
user> (input-stream (java.io.File. "/tmp/x"))
;;#<BufferedInputStream java.io.BufferedInputStream@21606a56>
user> (input-stream (java.io.File. "/tmp/x") :encoding "UTF-8")
;;#<BufferedInputStream java.io.BufferedInputStream@3e347b11>
as-file¶
接受一个 x
参数,返回一个 java.io.File
对象。 x
的类型可以是 java.lang.String
, java.io.File
, java.net.URL
和 java.net.URI
。
当 x
的类型是 java.net.URL
和 java.net.URI
时,协议必须是 file
。
当 x
是 nil
时,返回 nil
。
user> (use 'clojure.java.io)
nil
user> (.exists (as-file "/tmp"))
true
user> (.exists (as-file (java.io.File. "/tmp")))
true
user> (.exists (as-file (java.net.URL. "file:///tmp")))
true
user> (.exists (as-file (java.net.URL. "http://www.google.com")))
;;IllegalArgumentException Not a file: http://www.google.com clojure.java.io/fn--8210 (io.clj:67)
as-relative-file¶
接受一个 x
参数,据此返回相对路径字符串。 x
的类型可以是 java.lang.String
, java.io.File
, java.net.URL
和 java.net.URI
,跟 as-file
一样。
如果 x
不是用相对路径表示的,则抛出 IllegalArgumentException
。
user> (as-relative-path "./tmp")
"./tmp"
user> (as-relative-path "tmp")
"tmp"
user> (as-relative-path "/tmp")
;;IllegalArgumentException /tmp is not a relative path clojure.java.io/as-relative-path (io.clj:404)
user> (as-relative-path (java.io.File. "tmp-file"))
"tmp-file"
as-url¶
接受一个 x
参数,返回一个 java.net.URL
对象。 x
的类型可以是 java.lang.String
, java.io.File
, java.net.URL
和 java.net.URI
。
当 x
的类型是 java.lang.String
时, x
必须是一个合法的URL。
当 x
是 nil
时,返回 nil
。
如果提供了 km
参数,则按照 km
所列出的key进行join。
user> (use 'clojure.java.io)
nil
user> (as-url "http://baidu.com")
#<URL http://baidu.com>
user> (as-url (java.io.File. "/tmp"))
#<URL file:/tmp/>
user> (as-url (java.net.URI. "http://www.google.com"))
#<URL http://www.google.com>
user> (as-url "baidu.com")
;;MalformedURLException no protocol: baidu.com java.net.URL.<init> (URL.java:567)
default-streams-impl¶
IOFactory
的默认实现,抛出 IllegalArgumentException
。
delete-file¶
删除文件 f
。如果有第二个参数,且为真值,则当文件不存在的时候,不抛出异常。
user> (use 'clojure.java.io)
nil
user> (spit "/tmp/x" "123") ;; 创建一个文件,写入内容
nil
user> (slurp "/tmp/x") ;; 验证一下,已经存在
"123"
user> (delete-file "/tmp/x") ;; 删掉它
true
user> (slurp "/tmp/x") ;; 没有了
;;FileNotFoundException /tmp/x (No such file or directory) java.io.FileInputStream.open (FileInputStream.java:-2)
user> (delete-file "/tmp/x") ;; 抛异常
;;IOException Couldn't delete /tmp/x clojure.java.io/delete-file (io.clj:425)
user> (delete-file "/tmp/x" true) ;; 安静删除
true
file¶
根据参数创建一个 java.io.File
对象。
如果只有一个参数 arg
,返回对应的 java.io.File
对象。
如果有多个参数,第一个参数 parent
作为根目录;后续参数作为每一层子目录或文件,且必须是用相对路径表示的。
parent
, child
和 more
类型可以是 java.lang.String
, java.io.File
, java.net.URL
或者 java.net.URI
。
user> (use 'clojure.java.io)
nil
user> (file "/tmp")
#<File /tmp>
user> (file "/tmp" "a" "b")
#<File /tmp/a/b>
user> (file "/tmp" "a" (java.io.File. "../b"))
#<File /tmp/a/../b>
user> (file (java.net.URL. "file:///tmp") "a" (java.io.File. "../b"))
#<File /tmp/a/../b>
make-input-stream¶
根据 x
创建一个 java.io.BufferedInputStream
。
x
可以是 java.io.BufferedInputStream
, java.io.InputStream
, java.io.File
, java.net.URL
, java.net.URI
, java.lang.String
, java.net.Socket
或者 byte
数组。
当 x
是 java.lang.String
时,会先尝试把 x
解释成 java.net.URL
,如果失败,则是 java.io.File
。
opt
是一个map,定义选项,key可以是 :append
和 :encoding
。
user> (use 'clojure.java.io)
nil
user> (make-input-stream "/tmp/x" {})
;;#<BufferedInputStream java.io.BufferedInputStream@3a7aa9f6>
user> (make-input-stream (java.io.File. "/tmp/x") {})
;;#<BufferedInputStream java.io.BufferedInputStream@df077d2>
user> (make-input-stream (java.io.File. "/tmp/NO_SUCH_FILE") {})
;;FileNotFoundException /tmp/NO_SUCH_FILE (No such file or directory) java.io.FileInputStream.open (FileInputStream.java:-2)
make-output-stream¶
根据 x
创建 java.io.BufferedOutputStream
。
x
可以是 java.io.BufferOutputStream
, java.io.OutputStream
, java.io.File
, java.net.URL
, java.net.URI
, java.lang.String
, java.net.Socket
。
当 x
是 java.lang.String
时,会先尝试把 x
解释成 java.net.URL
,如果失败,则是 java.io.File
。
当 x
是 java.net.URL
和 java.net.URI
时,协议必须是 file
。
opt
是一个map,定义选项,key可以是 :append
和 :encoding
。
user> (use 'clojure.java.io)
nil
user> (make-output-stream "/tmp/x" {})
;;#<BufferedOutputStream java.io.BufferedOutputStream@5440bf04>
user> (make-output-stream (file "/tmp/x") {})
;;#<BufferedOutputStream java.io.BufferedOutputStream@4268d15>
make-parents¶
创建父目录,成功返回 true
。
f
和 more
可以是 java.lang.String
, java.io.File
, java.net.URL
或者 java.net.URI
。
user> (use 'clojure.java.io)
user> (make-parents "/tmp/a/b/c/d") ;; 创建 /tmp/a/b/c/
true
user> (make-parents "/tmp/a/x/" "y" "z/") ;; 创建 /tmp/a/x/y
true
make-reader¶
根据 x
创建一个 java.io.BufferedReader
。x
可以是 java.io.InputStream
, java.io.File
, java.net.URL
, java.net.URI
, java.lang.String
, java.net.Socket
, byte
数组或者 char
数组 。
当 x
是 java.lang.String
时,会先尝试把 x
解释成 java.net.URL
,如果失败,则是 java.io.File
。
opt
是一个map,定义选项,key可以是 :append
和 :encoding
。
user> (use 'clojure.java.io)
nil
user> (make-reader "http://baidu.com" {})
;;#<BufferedReader java.io.BufferedReader@5994a1e9>
make-writer¶
跟据 x
构造 java.io.BufferedWriter
。
x
可以是 java.io.BufferOutputStream
, java.io.OutputStream
, java.io.File
, java.net.URL
, java.net.URI
, java.lang.String
, java.net.Socket
。
当 x
是 java.lang.String
时,会先尝试把 x
解释成 java.net.URL
,如果失败,则是 java.io.File
。
当 x
是 java.net.URL
和 java.net.URI
时,协议必须是 file
。
opt
是一个map,定义选项,key可以是 :append
和 :encoding
。
user> (use 'clojure.java.io)
nil
user> (make-writer (java.io.File. "/tmp/xxx") {})
;;#<BufferedWriter java.io.BufferedWriter@c92fa70>
output-stream¶
根据 x
创建 java.io.BufferedOutputStream
。
x
可以是 java.io.BufferOutputStream
, java.io.OutputStream
, java.io.File
, java.net.URL
, java.net.URI
, java.lang.String
, java.net.Socket
。
当 x
是 java.lang.String
时,会先尝试把 x
解释成 java.net.URL
,如果失败,则是 java.io.File
。
当 x
是 java.net.URL
和 java.net.URI
时,协议必须是 file
。
opt
定义创建选项,key可以是 :append
和 :encoding
。
user> (use 'clojure.java.io)
nil
user> (input-stream (as-file "/tmp/x"))
;;#<BufferedInputStream java.io.BufferedInputStream@49160709>
user> (input-stream (as-file "/tmp/x") :append true)
;;#<BufferedInputStream java.io.BufferedInputStream@7f6ce64e>
reader¶
根据 x
创建一个 java.io.BufferedReader
。x
可以是 java.io.InputStream
, java.io.File
, java.net.URL
, java.net.URI
, java.lang.String
, java.net.Socket
, byte
数组或者 char
数组 。
当 x
是 java.lang.String
时,会先尝试把 x
解释成 java.net.URL
,如果失败,则是 java.io.File
。
opt
是一个map,定义选项,key可以是 :append
和 :encoding
。
user> (use 'clojure.java.io)
nil
user> (reader "http://baidu.com" :encoding "GB2312")
;;#<BufferedReader java.io.BufferedReader@620a9239>
resource¶
是用 ClassLoader
加载资源文件,返回 java.net.URL
对象。如果找不到资源文件,返回 nil
。
n
是资源名字字符串。 如果要使用特定的 ClassLoader
,需要使用 loader
参数。
user> (use 'clojure.java.io)
nil
user> (resource "project.clj")
#<URL jar:file:/Users/xiafei/.lein/self-installs/leiningen-2.0.0-preview10-standalone.jar!/project.clj>
writer¶
跟据 x
构造 java.io.BufferedWriter
。
x
可以是 java.io.BufferOutputStream
, java.io.OutputStream
, java.io.File
, java.net.URL
, java.net.URI
, java.lang.String
, java.net.Socket
。
当 x
是 java.lang.String
时,会先尝试把 x
解释成 java.net.URL
,如果失败,则是 java.io.File
。
当 x
是 java.net.URL
和 java.net.URI
时,协议必须是 file
。
opt
是一个map,定义选项,key可以是 :append
和 :encoding
。
user> (use 'clojure.java.io)
nil
user> (writer (java.net.URL. "file:///tmp/x") :append true)
;;#<BufferedWriter java.io.BufferedWriter@7274187a>
clojure.java.javadoc¶
add-remote-javadoc¶
(add-remote-javadoc package-prefix url)
添加路径 url
到远程 javadoc 路径的列表, package-prefix
是 URL 对应的 javadoc 的包名的开始部分。
user=> (use 'clojure.java.javadoc)
nil
user=> (add-remote-javadoc "org.apache.commons.csv." "http://commons.apache.org/proper/commons-csv/apidocs/index.html")
{"java." "http://java.sun.com/javase/6/docs/api/", "javax." "http://java.sun.com/javase/6/docs/api/", "org.apache.commons.codec." "http://commons.apache.org/codec/api-release/", "org.apache.commons.csv." "http://commons.apache.org/proper/commons-csv/apidocs/index.html", "org.apache.commons.io." "http://commons.apache.org/io/api-release/", "org.apache.commons.lang." "http://commons.apache.org/lang/api-release/", "org.ietf.jgss." "http://java.sun.com/javase/6/docs/api/", "org.omg." "http://java.sun.com/javase/6/docs/api/", "org.w3c.dom." "http://java.sun.com/javase/6/docs/api/", "org.xml.sax." "http://java.sun.com/javase/6/docs/api/"}
javadoc¶
(javadoc class-or-object)
使用阅览器打开 class-or-object
参数的相关 javadoc 文档。
优先打开本地文档( *local-javadocs*
),
其次才是远程文档( *remote-javadoc*
)。
user=> (use 'clojure.java.javadoc)
nil
user=> (javadoc String)
"http://java.sun.com/javase/6/docs/api/java/lang/String.html"
user=> (javadoc (java.util.Date.))
"http://java.sun.com/javase/6/docs/api/java/util/Date.html"
clojure.java.shell¶
sh¶
(sh & args)
传递给出的字符串到 Runtime.exec()
来启动一个子进程。
选项有:
:in
:给出下列合法的输入源给clojure.java.io/copy
, 比如,InputStream
,Reader
,File
,byte[]
或者String
, 来提供子进程的标准输入(stdin)。:in-enc
:给出一个字符串。作为字符的编码名(比如,UTF-8
或者ISO-8859-1
)来转换:in
中给定的字符串的编码,默认是UTF-8
。 如果:in
给出的字节数组(byte array), 那么它不会被解码,这个选项会被忽略。:out-enc
:选项可以是一个:bytes
或者一个String
, 如果给出的是一个String
, 它会被当作一个字符编码的名字,(比如,UTF-8
或者ISO-8859-1
)来转换子进程的标准输出的字符串编码,如果给出的是:bytes
, 子进程的标准输出会被存储到一个字节数组返回,默认是UTF-8
。:env
:用一个 map 重载进程的环境变量(env),如果你是一个受虐狂,你可以用一个String[]
。:dir
:用一个String
或者java.io.File
重载进程工作目录(dir)
你可以用 with-sh-env
或者 with-sh-dir
绑定 :env
或者 :dir
到多个操作。
sh
返回一个 map :
:exit
:子进程的返回码:out
:子进程的标准输出(stdout)(byte[]
或者String
):err
:子进程的标准错误(stderr)(用平台默认的编码的String
)
user=> (use '[clojure.java.shell :only [sh]])
;; Note: The actual output you see from a command like this will look messier.
;; The output below has had all newline characters replaced with line
;; breaks. You would see a big long string with \n characters in the middle.
user=> (sh "ls" "-aul")
{:exit 0,
:out "total 64
drwxr-xr-x 11 zkim staff 374 Jul 5 13:21 .
drwxr-xr-x 25 zkim staff 850 Jul 5 13:02 ..
drwxr-xr-x 12 zkim staff 408 Jul 5 13:02 .git
-rw-r--r-- 1 zkim staff 13 Jul 5 13:02 .gitignore
-rw-r--r-- 1 zkim staff 12638 Jul 5 13:02 LICENSE.html
-rw-r--r-- 1 zkim staff 4092 Jul 5 13:02 README.md
drwxr-xr-x 2 zkim staff 68 Jul 5 13:15 classes
drwxr-xr-x 5 zkim staff 170 Jul 5 13:15 lib
-rw-r--r--@ 1 zkim staff 3396 Jul 5 13:03 pom.xml
-rw-r--r--@ 1 zkim staff 367 Jul 5 13:15 project.clj
drwxr-xr-x 4 zkim staff 136 Jul 5 13:15 src
", :err ""}
user=> (use '[clojure.java.shell :only [sh]])
user=> (println (:out (sh "cowsay" "Printing a command-line output")))
_________________________________
< Printing a command-line output. >
---------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
nil
user=> (use '[clojure.java.shell :only [sh]])
nil
;; note that the options, like :in, have to go at the end of arglist
;; advantage of piping-in thru stdin is less need for quoting/escaping
user=> (println (:out (sh "cat" "-" :in "Printing input from stdin with funny chars like ' \" $@ & ")))
Printing input from stdin with funny chars like ' " $@ &
nil
clojure.xml¶
parse¶
(parse s)
(parse s startparse)
解析并加载源 s
, s
可以是一个文件, InputStream
,或者一个代表 URL 的字符串。
函数返回一棵 xml/element
类型的 struct-map
树,
struct-map
中包含键 :tag
、 :attrs
、 :content
,
以及访问函数 tag
、 attrs
、 content
。
startparse
用于指定解释所使用的解释器,
这个参数的值应该是一个函数:
函数接受一个源(source)和一个内容处理器(ContentHandler)作为参数,
并返回一个解释器作为函数的返回值。
(require '[clojure.xml :as xml]
'[clojure.zip :as zip])
;;convenience function, first sawn at nakkaya.com later in clj.zip src
(defn zip-str [s]
(zip/xml-zip (xml/parse (java.io.ByteArrayInputStream. (.getBytes s)))))
;;parse from xml-strings to internal xml representation
(zip-str "<a href='nakkaya.com'/>")
=>
[{:tag :a, :attrs {:href "nakkaya.com"}, :content nil} nil]
;;root can be rendered with xml/emit-element
(xml/emit-element (zip/root [{:tag :a, :attrs {:href "nakkaya.com"}, :content nil} nil]))
=>
<a href='nakkaya.com'/>
;;printed (to assure it's not lazy and performance), can be catched to string variable with with-out-str
clojure.repl¶
apropos¶
返回当前命名空间下所有与给定正则表达式或者字符串(str-or-pattern)相匹配的定义的序列
user=> (apropos "temp")
()
user=> (require 'clojure.template)
nil
user=> (apropos "temp")
(apply-template do-template)
;; 使用正则表达式
user=> (apropos #".*-temp*")
(apply-template do-template)
dir¶
分类打印出给定命名空间下的公共Var对象
user=> (require 'clojure.string 'clojure.repl)
user=> (clojure.repl/dir clojure.string)
blank?
capitalize
escape
join
lower-case
replace
replace-first
reverse
split
split-lines
trim
trim-newline
triml
trimr
upper-case
dir-fn¶
返回一个有序序列,该序列包括给定命名空间(ns)的公共Var对象。
user=> (require 'clojure.repl 'clojure.string)
user=> (pprint (clojure.repl/dir-fn 'clojure.string))
(blank?
capitalize
escape
join
lower-case
replace
replace-first
reverse
split
split-lines
trim
trim-newline
triml
trimr
upper-case)
nil
doc¶
打印出某个Var对象或者special form的文档信息
=> (doc map)
;; prints in console:
-------------------------
clojure.core/map
([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll, until any one of the colls is
exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments.
=> (doc clojure.core)
-------------------------
clojure.core
Fundamental library of the Clojure language
find-doc¶
如果某个Var对象的名字或者文档能与给定字符串或者正则表达式相匹配,那么打印该Var对象的文档信息。
user=> (find-doc "data structure")
-------------------------
clojure.core/eval
([form])
Evaluates the form data structure (not text!) and returns the result.
-------------------------
clojure.core/ifn?
([x])
Returns true if x implements IFn. Note that many data structures
(e.g. sets and maps) implement IFn
user=> (require 'clojure.string 'clojure.repl)
-------------------------
........
pst¶
英文全称为print stack trace
打印出最近一次被REPL捕获的异常信息
user=> (/ 1 0)
;; ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:156)
user=> (pst)
;; ArithmeticException Divide by zero
;; clojure.lang.Numbers.divide (Numbers.java:156)
;; clojure.lang.Numbers.divide (Numbers.java:3691)
;; user/eval13 (NO_SOURCE_FILE:7)
;; clojure.lang.Compiler.eval (Compiler.java:6619)
;; clojure.lang.Compiler.eval (Compiler.java:6582)
;; clojure.core/eval (core.clj:2852)
;; clojure.main/repl/read-eval-print--6588/fn--6591 (main.clj:259)
;; clojure.main/repl/read-eval-print--6588 (main.clj:259)
;; clojure.main/repl/fn--6597 (main.clj:277)
;; clojure.main/repl (main.clj:277)
;; clojure.main/repl-opt (main.clj:343)
;; clojure.main/main (main.clj:441)
nil
参考资料¶
Clojure 官方文档: clojure.org/documentation
Clojure API 手册: clojure.github.com/clojure
Clojure Cheat Sheet: clojure.org/cheatsheet
ClojureDocs ,由社区驱动的,带代码示例的 API 手册: clojuredocs.org