Clojure极简教程

简介与安装

Clojure是Lisp在JVM中运行的一种方言,所以可以预想其语言风格是括号之间的环环相扣。由于是在Java虚拟机中运行,所以应该事先下载JDK,然后安装Clojure。
在windows下有两种安装方法,其一是通过mvn对源码进行编译,

git clone https://github.com/clojure/clojure.git
cd clojure
mvn -Plocal -Dmaven.test.skip=true package

其二是通过安装Clojure管理程序Leiningen,可无痛安装。
通过Lein命令lein repl可以进入交互式窗口,可以快速验证一些较为简单的方法,有利于快速学习Clojure。
在这里插入图片描述

初步使用

运算符

若从未接触过Lisp,Clojure的语法可能显得比较吊诡,这里将运算符函数化了,即在Clojure中,如果想表达1+1,则相当于调用Add(1,1),从而保证所有功能皆可通过类似的语法来实现。这样也有一些好处,即在某些场合其实更加简洁:

user=> (+ 1 2)
3
user=> (+ 1 2 3 4 5)
15
user=> (+ 1,2,3)
6
user=> (* 1,2,4,)
8
user=> (> 1,2)
false
user=> (/ 2,4)
1/2

此外这样也能避免由于优先级可能引发的错误。

运算符功能备注
+ - * / rem加减乘除、求余
inc操作数+1(inc 1)相当于1++
dec操作数-1
max min最大值 最小值
=、not=、>、>=、<、<=顾名思义
and or not与或非
bit-and bit-or bit-not按位与或非
bit-xor按位亦或

需要注意,inc和dec只能传入一个参数,其他运算符则不然,当输入参数大于2时,相当于在每个变量之间均插入一个运算符,即(+ a b c) → \to a+b+c
在命令行中,运算符*还有一个特殊的含义,它可以引用我们此前输出结果。

user=> (+ 3 4)
7
user=> *1
7
user=> (* 4 5)
20
user=> (+ *1 *2)
27

变量与数据类型

变量声明相当于把一个值赋予一个符号,本质上也是值与符号的函数,这个函数即def。Clojure中的基元类型都是从Java中继承而来,所以支持整形浮点型布尔型等相关类型,并且无需声明即可调用。需要注意的是,其字符串需要用双引号进行标识。

除此之外,Clojure还支持四种集合类型,即list、vector、set、map。

user=> (def str "hello");字符串,clojure中用;进行注释
user=> (println str)
hello
nil
user=> (def testList '(1 2 3));list
user=> (def testVec [1 2 3]);vector
user=> (def testSet #{1 2 3});set
user=> (def testMap {:a 1, :b 2});map

需要注意,list的小括号前面需要加上一个单引号,否则会被识别成一个表达式从而报错。此外,如果新建一个不想其赋值的变量,也可以用单引号来延迟赋值。

user=> 'x
x

函数

Clojure中,函数通过关键字defn来定义,格式为(defn 函数名 [args] (),调用格式为(函数名 args)

user=> (defn test [x] (+ x 2))
#'user/test
user=> (test 5)
7

控制结构

循环

类型语法示例说明
while循环(while () (do ())(while (< x 5) (do (def x (inc x))))当x<5时x++
doseq(doseq [] ())(doseq [x testList] (println x))打印testList
dotimes(dotimes [] ())(dotimes [n 5] (def x (inc x))x=x+5,将后面的语句执行5次

实例代码

user=> (def x 1)
user=> (while (< x 5) (do (def x (inc x))))
user=> x
5
user=> (def testList '(1 2 3))
user=> (doseq [n testList] (println n))
1
2
3
nil
user=> (dotimes [n 5] (def x (inc x)))
user=> x
10

判断

类型语法示例说明
if(if () () ())(if(> a b) (println a) (println b))输出ab的最大值
if doif () (do ()) (do ())(if (> a b) (do (println a) (println True)) do())do内可以执行多种操作
case(case x a () b () c() …)(case x a (println “a”) b (println “b”) c(println “c”) …)如果是a则打印“a”…
cond(cond (cond1)(code1) (cond2)(code2))(cond (> x 1)(println">")(< x 1)(println"<")如果x>1,则输出">"…

示例代码:

user=> (def a 2)
user=> (def b 4)
user=> (if(> a b) (println a) (println b))
4
user=> (if (> a b) (do()()) (do (println b) (println true)))
4
true
user=> (case a 1 (println "1") 2 (println "2"))
2
user=> (cond (> a b) (println "a") (< a b) (println "b"))
b

需要注意一点,在coljure中,除了nil和false之外,所有值转化为布尔型均为true。

递归

从形式上来说,递归只是循环的一种特殊形式,区别在于递归需要调用函数体自身。但从运行的角度来说,如果遍历某一个数组testList,可以使用语句(doseq [x testList] (println x)),当x为testList第i个元素时,可以断定第i-1之前的所有状态都将毫无意义。但是递归语句则不然,在调用函数自身的过程中,需要将此前的所有状态压入栈中,这也是斐波那契数列爆内存的缘由。
可能是基于这个原因,一些函数式语言都将递归进行了特殊的声明,例如F#中需要用到关键字rec
在Clojure中,通过关键字recur实现递归操作,可以分别嵌入到loop循环和函数中。其中,Clojure中通过关键字defn来绑定一个函数

user=> (loop [i 0] (when (< i 5) (println i) (recur (+ i 2))))
0
2
4
nil
;Clojure中通过defn来绑定一个函数
user=> (defn print2 [i] (if (< i 5) (do (println i) (recur (+ i 2)))))
#'user/print2
user=> (print2 0)
0
2
4
nil
user=> (defn fact [x] (loop [n x f 1] (if (= n 1) f (recur (dec n) (* f n)))));定义递归函数
#'user/fact
user=> (fact 15)
1307674368000
user=> (loop [n 15 f 1] (if (= n 1) f (recur (dec n) (* f n))))
1307674368000

上面代码中,loop循环的含义为,n的初始值为15,f的初始值为1。如果n==1,则输出f,否则将n-1赋值给n,将f*n赋值给f。

异常

异常本质上来说也是一种判断,或者说异常是一个布尔值。

类型语法示例说明
assert(assert ())(assert false)当表达式为false时抛出异常
try throw finally(try (throw ())(finally ()))抛出这个throw块的异常并执行finally中语句
try catch finally(try ()(catch ())(finally ()))如果发生异常,则执行catch中语句,否则执行finally中语句
try throw catch

函数

此前稍微提到了一点函数,但并不足以体现出函数式语言的强大。

重载

Clojure中,通过增加函数体来实现类似重载的功能,其格式为

(defn 函数名 
	([args1] ()) 
	([args2] ())
	...)

例如

user=> (defn test ([] (println "empty")) ([a] (println a)))
user=> (test)
empty
user=> (test 1)
1

参数可变函数

Clojure中通过关键字&实现对输入参数可变的函数。例如

user=> (defn prints [ & num ] (println num))
user=> (prints 1 2 3 4 5)
(1 2 3 4 5)

匿名函数

Clojure通过关键字fn定义匿名函数。其格式为((fn [形参] (函数体)) 实参)

user=> ((fn [test] (println test)) "a")
a
nil

有了匿名函数,意味着可以直接把函数体写在主程序里,也就是说无论什么Clojure代码都可以写成一行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微小冷

请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值