Leiningen
将依赖项下载到本地并放置在适当的位置
启动REPL
通过设置*print-length*的值,来配置打印器一次最多打印多少项。
记录
匿名函数
其参数被命名为%1、%2,以此类推。对于第一个参数,可以使用%表示。
协议和数据类型● 协议(Protocol)作为Java接口的替代物,适于高性能的多态方法调度。
● 数据类型(Datatype)作为Java类的替代物,可用来创建抽象定义的实现,无论该抽象是由协议还是接口定义的。
一个变量的初始值被称为它的根绑定(root binding)
这里的bindings会在随后的exprs中生效,此外,exprs中最后一个表达式的值,就会成为let的返回值。
可以同时绑定整个容器与容器内的元素。在解构表达式内部,:as字句允许绑定整个闭合结构。
resolve会返回在当前命名空间中,解析符号得到的变量或是类。
in-ns来切换命名空间或创建新的命名空间
为了在当前命名空间中引入split别名,可以包含对split的命名空间clojure.string调用require,并用str用作其别名。
referrefer将其下的所有名称映射到当前命名空间中
use函数把require和refer合并成了一个步骤
:reload在调用require或use时,使用:reload标记用来强制重新加载一个程序库。
查找文档
使用repl库的source函数来查阅某个Clojure函数的源码
形式
比例(Ratio)类型
使用quot和rem函数来获取整型的商和余数
java
toUpperCase前面的句点告知Clojure,应该将其视为一个Java方法,而非Clojure函数。
读取器宏
函数
在参数列表中包含一个&号,你就能创建一个具有可变元数的函数。Clojure会把所有剩余的参数都放进一个序列中,并绑定到&号后面的那个名称上。
使用import把一个或者多个类名从Java包映射到当前命名空间中
import仅用于Java类。
Clojure会自动导入java.lang。
ns
ns宏将当前命名空间(可通过*ns*获取)设置为name,必要时还会创建这个命名空间。
references部分则可以包含:import、:require和:use。它们的工作方式与各自对应的同名函数类似。这样仅需要一个形式,就可以完成命名空间映射相关的所有设置。
new
Javadoc
分支结构与ifClojure的if会对其第一个参数进行求值。倘若结果逻辑为真,就返回对第二个参数求值的结果。
if do
do可以接受任意数量的形式,对这些形式逐个求值,并返回最后一个形式的求值结果。
do就是用来申明“注意,接下来会有副作用”的方式之一。
除了最后一个形式,do会把所有其他形式的返回值都给忽略掉,所以,这也就意味着对于那些被忽略掉的形式来说,必须具有某种副作用才有其存在的意义。
循环与loop/recur
loop与let的工作方式颇为相似,首先建立绑定bindings,然后对expres求值。
不同的地方是,loop设置了一个循环点(recursion point),随后这个循环点将成为特殊形式recur返回目标。
早先由Loop建立的绑定,会被recur重新绑定为新的值,并且控制程序流程返回到loop的顶端。
首次进入时,loop 把 result 和一个空向量进行了绑定,并把 x 绑定为 5。由于 x不为零,recur随后对名称x和result再次进行了绑定。
● result被绑定至一个新的向量,该向量是通过连接早先的result和x得到的。
● x则被绑定为前一个x的递减结果。
接下来程序流回到了loop的顶端。
由于这一次x仍然不为零,循环继续,再次对result加以累积并递减x。最终,x递减为零,if终结了循环并返回result。
去掉 loop,能让 recur 递归至函数的起始位置。这就使得编写那种把整个主体都用作隐式循环的函数变得极为容易。
meta
因为元数据:tag实在是太常用了,可以使用其简化形式^Classname,它会被展开为^{:tag Classname}。
在阅读函数定义时,如果发觉元数据会造成视觉混乱,也可以把它们放到最后。这得用到 defn 的一个变体,先是一或多个带括号的函数主体,随后紧接一个元数据映射表。
序列 first
序列 rest
序列cons
序列seq
序列next
(next aseq)等价于 (seq (rest aseq))
对向量使用rest或cons时,得到的结果是一个序列,而非向量。
序列函数返回的总是序列。
sorted-set
sorted-map
conj
conj 会向容器添加一个或是多个元素
into
into 则会把容器中的所有元素添加至另一个容器。添加数据时,conj和into都会根据底层数据结构的特点选取最高效的插入点。
对于列表而言,conj和into会在其前端进行添加。
对于向量,conj和into则会把元素添加至末尾。
range生成一个从start开始到end结束的序列,每次的增量为step。
范围包含了start,但并不包含end。没有指定的话,start默认为0,step默认为1。
repeat
repeat函数会重复n次元素x。
iterate
iterate起始于值x,并持续地对每个值应用函数f,以计算下一个值,直至永远。
take
take会返回一个包含了容器中前n项元素的惰性序列。
cyclecycle函数接受一个容器,并无限的对其进行循环。
interleaveinterleave函数接受多个容器作为参数,并产生一个新的容器,这个新容器会从每个参数容器中交错地提取元素,直至其中某个容器元素被耗尽。
当其中的某个容器被耗尽时,interleave就会终止。
interpose把输入序列中的每个元素用分隔符隔开,并作为新的序列返回。
join(apply str ...)
创建容器
set函数
vecvec接受容器作为参数,而非可变的参数列表。
filter
take-while
complementcomplement 会反转另一个函数的行为。
drop-while函数
drop-while 从序列的起始位置开始,逐个丢弃元素,直至谓词判定为真,然后返回序列剩余的部分。
split-at和split-with能把一个容器一分为二
所有take-、split-和drop-打头的函数,返回的都是惰性序列。
every?every?要求其他谓词对序列中的每个元素都必须判定为真。
some
只要有一个元素被谓词判定为非假,some就会返回这个值,如果没有任何元素符合,则some返回nil。
注意,some没有以问号结尾。尽管总被当作谓词使用,但some并非谓词。
some 返回的是第一个符合项的值,而非 true。由于 even?本身就是一个谓词,所以当some与even?配对使用时,其间的差异并不明显。
映射函数map
map接受一个源容器coll和一个函数f作为参数,并返回一个新的序列。该序列的所有元素,都是通过对coll中的每个元素调用f得到的。
还可以传入多个容器给map。在这种情况下,f必须是一个多参函数。map会从每个容器分别取出一个值,作为参数来调用f,直到数量最少的那个容器被耗尽为止。
归纳函数reduce
其中f是一个接受两个参数的函数。reduce首先用coll的前两个元素作为参数来调用f,然后用得到的结果和第三个元素作为参数,继续调用f,以此类推。
sort或sort-by对容器进行排序
sort 会依据元素的自然顺序对容器进行排序,sort-by 则会对每个元素调用 a-fn,再依据得到的结果序列来进行排序。
列表解析(list comprehension)基于一个已存在的列表来创建新的列表。
解析式描述了结果列表必须满足的性质。
一个列表解析会包含以下内容。
● 输入列表。
● 输入列表中元素所对应的占位变量。
● 作用于元素的谓词。
● 一个输出形式,它负责基于那些满足谓词要求的列表元素来产生输出。
Clojure把列表解析的概念泛化为了序列解析(sequencecomprehension)。
for 接受一个向量作为参数,该参数是由一系列 binding-form/coll-expr 和可选的filter-expr组成的,然后是依据表达式expr来产生结果序列。
序列解析中,Clojure会从按照右到左的顺序,来遍历绑定表达式。由于在绑定形式中,rank列于file的右手边,所以rank会迭代的更快。
doall强制求值
doall迫使Clojure遍历序列中的元素,并把这些元素作为结果返回。
dorun
dorun遍历容器中的元素,但它不会把穿过的元素保留在内存中。
dorun可以遍历那些超过了内存容许范围的超大容器。
re-seq
re-seq会把匹配结果暴露为一个不可变的序列。
file-seq深度优先的遍历方式
line-seq将Java的Reader以行的方式进行序化
with-open
xml-seq
以序列的方式来查看树。
列表函数peek和pop分别用于取出列表的第一个元素,和其余的那些元素。
peek等同于first,但pop则与rest不同。如果是空序列,pop会抛出一个异常。
向量函数向量也支持peek和pop,但它们是从向量的末尾开始处理元素的。
向量自身也是函数。它接受一个索引作为参数并返回对应的值,或是当索引超过边界时,抛出一个异常。
assoc会在指定的索引位置,关联一个新的值。
subvec会返回向量的一个子向量。
若未指定end,则默认为向量的末尾。
映射表函数keys将所有的键作为序列返回,vals则将所有的值作为序列返回。
get会返回键对应的值,或者是返回nil。
映射表同时也是一个函数,以它自己的键作为参数。
关键字同样也是函数。他接受一个容器作为参数,并在这个容器中查找其自身。
如果在映射表中查找一个键并返回了 nil,无法确认究竟是键对应的值为 nil,还是这个键在映射表中根本就不存在。contains?函数就可以解决这个问题,它只是单纯的检测某个键是否存住。
映射表,nil是里面的一个合法值
● assoc返回新增了一个键值对的映射表。
● dissoc返回移除了某些键的映射表。
● select-keys返回一个映射表,仅保留了参数传入的那些键。
● merge可以合并映射表。如果多个映射表包含了同一个键,那么最右边的获胜。
merge-with
merge-with与merge很类似,除了当两个或以上的映射表中有相同的键时,你能指定一个你自己的函数,来决定如何合并这个键对应的值。
集合函数 除了clojure命名空间中可用于集合的函数外,Clojure还提供了一组位于clojure.set命名空间的函数。
● union返回的集合,包含了所有输入集合中的元素。
● intersection返回的集合,其所有元素都曾同时出现于多个输入集合中。
● difference 返回的集合,其所有元素都出现于第一个输入集合,但却未出现于第二个中。
● select返回所有元素都能与给定谓词相匹配的一个集合。
并集和差集同为集合论的一部分,除此以外它们也是关系代数(relationalalgebra)的组成部分,而后者则是诸如SQL这样的查询语言的根基。
关系代数包括六个基本的运算符:并集和差集,加上重命名(rename)、选择(selection)、投影(projection)和叉积(crossproduct)。
关系代数与关系型数据库
rename函数可以用来给键(数据库的列)重新命名,基于一个映射表,把原来的名称改成新的。
project函数返回的那些映射表中,仅包含与参数匹配的键。
如果两个关系中的键名不匹配,可以传入一个keymap,将relation-1中的键名映射到relation-2中对应的键。如可以将使用:country的composers,与使用:nation的nations相连接。
letfn宏
letfn与let很像,只不过它是专门用来指定局部函数的。每个声明于letfn中的函数,都可以调用自身或是其他属于同一个letfn块的函数。
lazy-seq宏
仅当需要时,lazy-seq 才会去调用它的 body,也就是说,当 seq 被直接或是间接的调用时,lazy-seq会为后续的调用缓存结果。
lazy-cat这个定义使用了lazy-cat,它和concat非常相像,除了它的参数仅当需要时才会被求值。
partition
partition能把容器拆分为一个一个大小都是size的块。
可选的step参数指明了向下一个块移动之前,partition应该在容器中向后移动多远。如果没有指定,则step就与size相同。
def的形态defn、defmacro和defmulti。它们最终实际上都不过是对def这个特殊形式的包装罢了。
defonce能够保证一个变量一定存在,且会为其设置根绑定值,前提是它还没有被设置过。
defn-与defn的作用类似,只不过,它产生的是一个私有函数,只能在定义它的命名空间中可见。
compcomp用于组合两个或更多的函数。
首先它用自己的参数调用最右端的函数,然后将获得的结果作为参数传给次右端的函数,以此类推。
partialpartial创建了一个新的函数
partial会对一个函数进行部分应用(partial application)。当执行partial时,可以指定一个函数f和该函数的部分参数。随后当调用这个由partial创建的函数时,可再行指定剩余的那些参数。
faux-curry部分求值
declare宏允许在一行代码里同时创建两个变量(没有初始绑定)。