0%

七周七语言之-Clojure

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
Dload Upload Total Spent Left Speed
100 18.8M 100 18.8M 0 0 717k 0 0:00:26 0:00:26 --:--:-- 961k
Installing libs into /usr/local/lib/clojure
Installing clojure and clj into /usr/local/bin
Installing man pages into /usr/local/share/man/man1
Removing download
Use clj -h for help.

此时在/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的构建管理工具leiningen
cd /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中,这两者是一种东西,是等价的。一类类型和内嵌容器(布尔,字符,字符串,集合,映射,向量)这些东西都是形式。

  1. 字符串/字符

    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=>

    字符串和其他语言没有什么两样,但是字符的表示方法是\cprint函数也和其他语言的输出函数无区别,str是用来连接字符成字符串的。

  2. 布尔值和逻辑表达式

    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分支,可以省略。

  1. 列表(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

  2. 向量(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)
  3. 集合(Set)

特性

延迟执行

user=> (/ 1 3)
1/3
user=> (class(/ 1 3))
clojure.lang.Ratio

可以看到在执行不能除尽的整数除法计算时,clojure不会立即计算结果,而是会通分化简,这在执行计算时可以保留精度。

Disqus评论区没有正常加载,请使用科学上网