Why? 为了更好的了解并发和函数式语言.
简介
Clojure是一套现代的Lisp语言的动态语言版(Lisp方言)
- 函数式语言, 编译型语言
- 最开始Clojure运行在JVM上, 所有有很好的扩平台特性.
- 函数式语言的
不变性使它们很好的支持并发
环境部署
- 下载IntelliJ IDEA
- Preference -> Plugins -> Browse Repositories -> 查找Cursive
- 重启IDEA
使用Leiningen作为Clojure的命令行构建工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| # 安装 $ brew update $ brew install leiningen # 查看lein的命令 $ lein help # 进行Clojure的REPL $ lein repl # 使用Clojure创建一个可运行的Clojure的项目 $ lein new app hello-world # 进入创建的项目, 然后执行 $ lein run # 在项目内对代码打包 $ lein uberjar # 使用java运行jar包 $ java -jar target/uberjar/hello-world-0.1.0-SNAPSHOT-standalone.jar
|
语法
每学习一门新的编程语言, 我觉得都应该对应以前学习过的语法, 找出不同之处, Clojure的语法感觉还是挺个性的.
Clojure代码由一个个form组成, 即写在小括号里的由空格分开的一组语句
- Clojure的注释以
;(分号)开始
1 2
| ; operator未操作符(函数或者加减乘除等), 后面均为操作数 (operator operand1 operand2 ... operandn)
|
变量
1 2 3 4 5 6
| ; 分号后均为注释, def定义一个变量 ; 其中user=> 为lein repl给出的命令行提示符(下同) user=> (def a 1) #'user/a user=> (println a) 1
|
基本类型和操作
- Clojure基本数据类型包括
Number, String, Map, Keyword, Vector, List, Set, 均为immutable, 对基本数据类型的更改都会在生成一个新的拷贝
- 其中关键字
Keyword类似于Ruby张的符号(symbol)或Java中的保留字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| user=> (def a 1) ; 整数, java.lang.Long user=> (def s "hello") ; 字符串, java.lang.String :a ; keyword user=> (def v [1 2 3]) ; Vector数组, clojure.lang.PersistentVector user=> (def a `(1 2 3)) ; List链表, clojure.lang.PersistentList user=> #{1 2 3} ; 使用#{}创建一个Set, 类型为clojure.lang.PersistentHashSet ; 通过Vector来创建Set user=> (set [1 2 3]) # 创建一个hashMap, :a是clojure.lang.Keyword类型, 等下会讲到 user=> (def mymap {:a 1 :b 2 :c 3}) #'user/mymap user=> (class mymap) clojure.lang.PersistentArrayMap ; map支持很多类型的key,但推荐使用keyword类型作为map的key ; keyword类型和字符串类似, 但做了一些优化。 (class :a) ; => clojure.lang.Keyword
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ; 判断是否是集合 user=> (coll? '(1 2 3)) ; => true user=> (coll? [1 2 3]) ; => true ; 判断是否为List user=> (seq? '(1 2 3)) ; => true user=> (seq? [1 2 3]) ; => false ; cons用以向列表或向量的起始位置添加元素 user=> (cons 4 [1 2 3]) ; => (4 1 2 3) user=> (cons 4 '(1 2 3)) ; => (4 1 2 3) ; 向集合中插入元素, 对于List, 数据会在起始位置插入, 而对于Vector, 则在末尾位置插入 user=> (conj [1 2 3] 4) user=> (conj '(1 2 3) 4) user=> (conj #{1 2 3} 4) #{1 4 3 2} ; 从Map中查找元素 user=> (def mymap {:a 1 :b 2 :c 3}) user=> (mymap :a) 1 user=> (mymap :d) ;查找不到的元素会返回nil nil ; dissoc溢出Map中的元素 user=> (dissoc mymap :a) {:b 2, :c 3}
|
1 2 3 4 5 6 7
| (+ 1 1) ; 加 (- 2 1) ; 减 (* 1 2) ; 乘 (/ 2 1) ; 除 (= 1 1) ; 等号 (not true) ; 逻辑非 (< 1 2) ; < > =
|
1 2 3
| (if condition-form then-form optional-else-form)
|
切记: Cloju使用nil和false表示逻辑假, 其他所有值的均为真
1
| user=> (if false "hello" "world") ; 输出结果为world
|
函数
- 使用
defn来自定义一个函数
- 函数定义使用
(defn 函数名 函数注释 参数列表, 函数体)
- 函数体可以由多个form构成, Clojure将最后一个form的执行结果作为返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| # hello为函数名, ""中为函数注释, []为参数, 函数体可以包含多个form user=> (defn hello "just a test method" [sentence] (println sentence)) #'user/hello user=> (hello "andrew liu") andrew liu ; 变参函数,即把&后面的参数全部放入一个序列 user=> (defn print0 [& args] #_=> (str "parameter is " args)) #'user/myprint user=> (print0 "Andrew" "Liu") "parameter is (\"Andrew\" \"Liu\")" ; 固定参数和可变参数的结合 user=> (defn print1 [name & args] #_=> (println name) #_=> (str args)) #'user/print1 user=> (print1 "Andrew" 1 2 3) Andrew "(1 2 3)"
|
1 2 3 4 5 6 7 8 9 10 11
| user=> (defn func-overload #_=> ([] (str "no arg")) #_=> ([name] (str "arg " name)) #_=> ([name_a name_b] (str "arg " name_a " " name_b))) #'user/func-overload user=> (func-overload) "no arg" user=> (func-overload "andrew") "arg andrew" user=> (func-overload "andrew" "liu") "arg andrew liu"
|
使用fn来创建一个匿名函数, 在Python中使用lamda来创建匿名函数.
1 2 3 4 5
| # 定义一个匿名函数变量 user=> (def no_name (fn [name] (str "hello " name))) #'user/no_name user=> (no_name "andrew") "hello andrew"
|
函数式
1 2 3 4 5 6 7 8 9 10 11
| ; 对给定序列的每一个元素做inc操作(映射) user=> (map inc [0 1 2 3]) (1 2 3 4) ; reduce操作有三个参数, 第一个为双参数函数, 第二个为初始值, 第三个为集合, user=> (reduce (fn [x y] (+ x y)) 0 [1 2 3]) 6 ; partial绑定一个参数为固定值 user=> (map (partial * 2) [1 2 3 4]) (2 4 6 8)
|
宏
并发
- Future 可以让我们在另一个线程去执行任务,它是异步执行的,所以调用了future之后会立即返回
- Promise是一个承诺,我们定义了这个promise,就预期后续会得到相应的result
参考链接