Clojure是一门运行在JVM上的lisp方言。
Common Lisp
common lisp安装sudo pacman -S common-lisp
,选择[1]的clisp
实现,输入clisp
进入交互shell环境。
Clojure安装
- 环境:Archlinux
- jdk: 12
首先安装clojure语言,使用pacman安装会找不到类途径,于是我还是手动安装了。官网提供的安装方式curl -O https://download.clojure.org/install/linux-install-1.10.1.447.sh
sudo chmod +x linux-install-1.10.1.447.sh
sudo ./linux-install-1.10.1.447.sh
% Total % Received % Xferd Average Speed Time Time Time Current |
此时在/usr/local/lib/clojure/libexec
下出clojure-tools-1.10.1.447.jar
,clojure本质上就是一个jar包,完全可以用java直接启动:java -cp clojure-tools-1.10.1.447.jar clojure.main
Clojure 1.10.1
user=>
然后安装clojure的构建管理工具leiningencd /usr/bin
sudo wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein
sudo chmod a+x lein
lein
此时会执行自安装脚本,挂梯子https代理,可能会出现如下的错误:https://github.com/technomancy/leiningen/releases/download/2.5.0/leiningen-2.5.0-standalone.jar (exit code 7) It‘s possible your HTTP client‘s certificate store does not have the correct certificate authority needed. This is often caused by an out-of-date version of libssl. It‘s also possible that you‘re behind a firewall and haven‘t set HTTP_PROXY and HTTPS_PROXY.“
解决方法:export HTTP_CLIENT="wget --no-check-certificate -O"
。官网是这么说的,不过我这里行不通,报错如下,提示找不到Clojure的类:Error: Could not find or load main class clojure.main
Caused by: java.lang.ClassNotFoundException: clojure.main
解决办法,手动下载leiningen的jar包,去leiningen的release下载最新的standlone包,大概20M。下载后改名zip为jar,配置环境变量export LEIN_HOME=$HOME/.lein
export LEIN_JAR=$LEIN_HOME/self-installs/leiningen-2.9.1-standalone.jar
然后移到下载的jar包对应目录下。
此时clojure -V
下载maven依赖,然后lein repl
,若干秒后,启动repl。$ lein repl
nREPL server started on port 39511 on host 127.0.0.1 - nrepl://127.0.0.1:39511
REPL-y 0.4.3, nREPL 0.6.0
Clojure 1.10.0
Java HotSpot(TM) 64-Bit Server VM 12.0.1+12
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
特性
基本运算
clojure作为一门lisp方言,每个语句都是一个表,用括号表示,这是一种前缀表达式,即运算符(函数)写在最前面,之后是参数。
简单的逻辑运算user=> (- 1)
-1
user=> (+ 1 2)
3
user=> (* 1 3)
3
user=> (/ 1 3)
1/3
user=> (class(/ 1 3))
clojure.lang.Ratio
clojure支持变长的参数列表,会顺序执行:user=> (+ 1 1)
2
user=> (+ 1 1 3)
5
user=> (+ 1 1 3 4 5)
14
clojure或根据括号的嵌套顺序,依次执行运算user=> (+ (+ 1 1) (/ 3 4))
11/4
这在判断一个列表是否排序时,可以写的很精妙:user=> (< 1 2 3 4)
true
user=> (< 1 2 4 3)
false
类型系统
clojure支持强类型,也支持动态类型。clojure中的基本构成是形式(Form),形式可以看成是语法的一部分,clojure解析代码时,会将程序编译成多份叫做形式的片段,然后编译或者解释。clojure中。不区分代码和数据,在lisp中,这两者是一种东西,是等价的。一类类型和内嵌容器(布尔,字符,字符串,集合,映射,向量)这些东西都是形式。
字符串/字符
user=> (print "hello\nI'am XUranus")
hello
I'am XUranusnil
user=> (println "hello\nI'am XUranus")
hello
I'am XUranus
nil
user=> (println \H\e\l\l\o)
Hello
nil
user=> (str \H \e \l \l \o)
"Hello"
user=>字符串和其他语言没有什么两样,但是字符的表示方法是
\c
,print
函数也和其他语言的输出函数无区别,str
是用来连接字符成字符串的。布尔值和逻辑表达式
user=> (class (= 1 1))
java.lang.Boolean
user=> (= 1 2)
false
user=> (= 1 1.0)
false
user=> (= 1 1)
true
user=> (if (= 1 1) (println "one equals one"))
one equals one
nil
user=> (if (< 1 1) (println "one equals one") (print "one don't equals ones"))
one don't equals onesnil
nil支持已经可以发觉JVM在clojure中的体现,因为clojure底层实际上就是jvm,他的类型也和java是对应的。
clojure中的逻辑分支语句的格式就是(if <bool> <express1> <express2>)
,根据bool判断执行分支,expression2默认是else分支,可以省略。
列表(List)
列表是有序元素的的集合,元素可以是任何东西,但clojure中列表一般用做代码,向量用做数据user=> (class '(1 2 3))
clojure.lang.PersistentList
user=> (class (list 1 2 3))
clojure.lang.PersistentList
user=> (= (list 1 2 3) '(1 2 3) )
true虽然clojure的语句就是列表,比如
(+ 1 2)
就是一个列表,但是这种列表是用来代表函数要被执行求值的,所以创建一个一般的列表不能这么写。
上述展示了两种表的创建方法,前者方式为引用(quoting),可以看到他们底层类型一致,值也完全相当等,这两种创建是等价的。
表支持几种基本算子,first
,last
,rest
,cons
,分别表示第一个数据,最后一个数据除了第一个数据之后的数据,链接构造新表。user=> (first '(:one :two :three :four))
:one
user=> (last '(:one :two :three :four))
:four
user=> (rest '(:one :two :three :four))
(:two :three :four)
user=> (cons :zero '(:one :two :three :four))
(:zero :one :two :three :four)注意:clojure中的动态类型中,0和””都可以被映射为true,而nil映射为false
向量(Vector)
向量是有序的可以循秩访问的元素集合,支持了随机访问的优化,用方括号扩起来表示。作为i表的兄弟,向量也支持表的基本算子,向量额外支持了随机访问算子:nth
user=> [:one :two :three :four]
[:one :two :three :four]
user=> (class [:one :two :three :four])
clojure.lang.PersistentVector
user=> (first [:one :two :three :four])
:one
user=> (last [:one :two :three :four])
:four
user=> (rest [:one :two :three :four])
(:two :three :four)
user=> (cons :zero [:one :two :three :four])
(:zero :one :two :three :four)
user=> (nth [:one :two :three :four] 3)
:four
user=> (nth [:one :two :three :four] 4)
Execution error (IndexOutOfBoundsException) at user/eval2173 (REPL:1).
null向量还支持合并两个向量:
user=> (concat [1 2 3 4] [:one :two :three :four])
(1 2 3 4 :one :two :three :four)集合(Set)
特性
延迟执行
user=> (/ 1 3) |
可以看到在执行不能除尽的整数除法计算时,clojure不会立即计算结果,而是会通分化简,这在执行计算时可以保留精度。