几分钟内学习 Clojure

来源:互联网 发布:阿里云centos6升级到7 编辑:程序博客网 时间:2024/06/12 01:50

Clojure是JVM上的一个LISP语言变种。它比Common Lisp更强调纯函数式编程,但提供了一些STM工具以处理它所引入的状态问题。

这个组合使并行处理更加简单,并且通常是自动的。

(你需要Clojure 1.2或以上版本。

Sweelia
Sweelia
翻译于 2年前

1人顶

 翻译的不错哦!

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
; 分号作为注释的开始
 
; Clojure 用一种把元素用括号括起来的像列表一样的方式来书写,元素之间用空格隔开
; clojure 解释器会把第一个元素当做是函数或者宏调用,其他的都作为参数
; 下面这个函数用于设置当前的命名空间
(ns test)
 
; 更多基本的例子:
 
; str 函数会用它所有的参数创造一个字符串
(str "Hello"" " "World") ; => "Hello World"
 
; 数学运算很直观,不过是前缀表达式
(+ 1 1) ; => 2
(- 2 1) ; => 1
(* 1 2) ; => 2
(/ 2 1) ; => 2
 
;  相等比较使用 “=”符号
(= 1 1) ; => true
(= 2 1) ; => false
 
; 你也不必担心逻辑运算
(not true) ; => false
 
; 嵌套方式正如你预料的那样
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2
 
; 类型系统
;;;;;;;;;;;;;
 
; Clojure 使用java对象类型来表示 布尔值、字符串和数字
; 使用 `class`函数来检测它们.
(class 1) ; 整形字面值默认是java中的Long类型
(class 1.); 浮点字面值对应着java中的Double类型
(class ""); 字符串总是用双引号括起来,并且对应着java中的Sring类型
(class false) ;布尔值对应着java中的Boolean类型
(class nil); null值被称为 nil(英语含义:无、零点)
 
; 如果你想创建一列数据字面值, 使用一个单引号 ' 来防表达式被解析执行
'(+ 1 2) ; => (+ 1 2) ;这里没有返回3
; (上面表达式和(quote (+ 1 2)) 等价,不过更简洁
 
; 你可以运算一个引用列表
(eval'(+ 1 2)) ; => 3
 
; 集合和序列
;;;;;;;;;;;;;;;;;;;
 
; 向量和列表也是java类哦!!
(class [1 2 3]); => clojure.lang.PersistentVector
(class '(1 2 3)); => clojure.lang.PersistentList
 
;书写一个列表形如(1 2 3)一样简单, 但是我们不得不把它“引”(前面加个单引号)起来
;这样就能防止解释器把它当做一个函数来解析
;另外,(list 1 2 3) 和 '(1 2 3) 等价
 
;列表和向量都是集合:
(coll? '(1 2 3)) ; => true
(coll? [1 2 3]) ; => true
 
; 只有列表是序列.(序列是有顺序的)
(seq? '(1 2 3)) ; => true
(seq? [1 2 3]) ; => false
 
; 序列是列表一种逻辑上的接口,可以懒加载.
"懒"意味着可以定义无穷序列,就像下面一样:
(range 4) ; => (0 1 2 3)
(range) ; => (0 1 2 3 4 ...) (一个无穷序列)
(take 4 (range)) ;  (0 1 2 3)
 
; 使用cons 来追加一个元素到列表或者向量的头部
(cons 4 [1 2 3]) ; => (4 1 2 3)
(cons 4 '(1 2 3)) ; => (4 1 2 3)
 
; 使用conj追加一个元素到列表的头部,或者向量的尾部,
(conj [1 2 3] 4) ; => [1 2 3 4]
(conj '(1 2 3) 4) ; => (4 1 2 3)
 
; 使用concat来连接列表和向量
(concat [1 2] '(3 4)) ; => (1 2 3 4)
 
; 使用filter, map 来进行列表计算
(map inc [1 2 3]) ; => (2 3 4)
(filter even? [1 2 3]) ; => (2)
 
; 使用reduce 来进行化繁为简  (map/reduce思想就来自于lisp)
(reduce + [1 2 3 4])
; = (+ (+ (+ 1 2) 3) 4)
; => 10
 
; Reduce 可以使用一个初始值
(reduce conj [] '(3 2 1))
; = (conj (conj (conj [] 3) 2) 1)
; => [3 2 1]
 
; 函数
;;;;;;;;;;;;;;;;;;;;;
 
; 使用fn来创建一个函数。所有的函数都有返回值,就是它的最后一个表达式
(fn [] "Hello World") ; => fn
 
; (你需要额外的括号去调用它)
((fn [] "Hello World")) ; => "Hello World"
 
;你可以使用def来创建变量
(def x 1)
x ; => 1
 
; 将函数赋值给一个变量
(def hello-world (fn [] "Hello World"))
(hello-world) ; => "Hello World"
 
; 你可以使用defn来简化定义过程
(defn hello-world [] "Hello World")
 
;[] 是函数的参数列表
(defn hello [name]
  (str "Hello " name))
(hello "Steve") ; => "Hello Steve"
 
; 你也可以使用下面这种简写方式
(def hello2 #(str "Hello " %1))
(hello2 "Fanny") ; => "Hello Fanny"
 
; 你可以创建拥有可变参数的函数
(defn hello3
  ([] "Hello World")
  ([name] (str "Hello " name)))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"
 
; 函数允许将参数打包成列表 (有点类似python中的*)
(defn count-args [& args]
  (str "You passed " (count args) " args: " args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
 
; 你可以将普通参数和列表参数混合使用
(defn hello-count [name & args]
  (str "Hello " name ", you passed " (count args) " extra args"))
(hello-count "Finn"1 2 3)
; => "Hello Finn, you passed 3 extra args"
 
 
; 哈希表
;;;;;;;;;;
 
(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap
 
; 关键字类似字符串,但是做了一些性能上的优化
(class :a) ; => clojure.lang.Keyword
 
; Maps 的键可以是任意类型,但是通常推荐使用keywords
(def stringmap (hash-map "a"1, "b"2, "c"3))
stringmap  ; => {"a"1, "b"2, "c"3}
 
(def keymap (hash-map :a 1 :b 2 :c 3))
keymap ; => {:a 1, :c 3, :b 2} (不保证顺序)
 
; 顺便说一下, 逗号只是为了看着更清晰,其他都和空格一样,什么都不做.
 
; 从一个map中检索一个值,可以直接把这个map当做函数调用(这个NB)
(stringmap "a") ; => 1
(keymap :a) ; => 1
 
; 关键字也可以当做函数来调用,从一个map中检索值(这个更NB)
(:b keymap) ; => 2
 
; stings 可没有这个功能,所以下面会抛出异常。(这也是为什么推荐使用keywords)
;("a"stringmap)
; => Exception: java.lang.String cannot be cast to clojure.lang.IFn
 
; 检索一个不存在的值会返回nil
(stringmap "d") ; => nil
 
; 使用assoc 向一个map中添加新的键值对。
(assoc keymap :d 4) ; => {:a 1, :b 2, :c 3, :d 4}
 
; 请记住, clojure 类型是不可变的!
keymap ; => {:a 1, :b 2, :c 3}
 
; 使用dissoc 来删除key(可以删除多个)
(dissoc keymap :a :b) ; => {:c 3}
 
; 集合
;;;;;;
 
(class #{1 2 3}) ; => clojure.lang.PersistentHashSet
(set[1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}
 
; 使用con来添加新值
(conj #{1 2 3} 4) ; => #{1 2 3 4}
 
; 使用disj删除原有值
(disj #{1 2 3} 1) ; => #{2 3}
 
; 直接将set当做函数来测试是否包含某个值(NB)
(#{1 2 3} 1) ; => 1  (有就返回原有的值)
(#{1 2 3} 4) ; => nil (没有就返回nil)
 
; clojure.sets 命名空间包含更多的函数
 
; 一些有用的形式
;;;;;;;;;;;;;;;;;
 
; clojure中的逻辑结构都是宏, 看起来也没什么不同
(iffalse "a" "b") ; => "b"
(iffalse "a") ; => nil
 
; 使用let来创建临时绑定
(let[a 1 b 2]
  (> a b)) ; => false
 
; 执行多条语句,返回最后一条语句
(do
  (print "Hello")
  "World") ; => "World"(prints "Hello")
 
; 所有的函数都包含一个隐式的do
(defn print-and-say-hello [name]
  (print "Saying hello to " name)
  (str "Hello " name))
(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff")
 
let绑定也是哦
(let[name "Urkel"]
  (print "Saying hello to " name)
  (str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel")
 
; 模块
;;;;;;;;;;;;;;;
 
; 使用“use”来获得一个模块中所有的函数
(use 'clojure.set)
 
; 现在我们可以使用集合操作
(intersection #{1 2 3} #{2 3 4}) ; => #{2 3}  求交集
(difference #{1 2 3} #{2 3 4}) ; => #{1}   求差集
 
; 你可以只导入一个函数子集(例如下面只包含交集函数)
(use '[clojure.set:only [intersection]])
 
; 使用reqire来导入一个模块
(require 'clojure.string)
 
; 使用/从一个模块中调用函数
(clojure.string/blank"") ; => true
 
; 你可以在导入模块的时候自定义名称
(require '[clojure.string :as str]) 
(str/replace"This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst."
; (#"" denotes a regular expression literal)
 
; 你可以使用":require"从一个命名空间中引入模块(use也可以,但是别这么做)
; 如果你使用:require的话,就没必要把模块“引”(前面加个单引号)起来了.
(ns test
  (:require
    [clojure.string :as str]
    [clojure.set:as set]))
 
; Java
;;;;;;;;;;;;;;;;;
 
; java 拥有一个庞大的各种用途的标准库,你一定迫不及待想学习如何在clojure中使用这些库
 
; 使用import类引入java模块(这个还好没变化)
(importjava.util.Date)
 
; 你也可以从一个命名空间中引入
(ns test
  (:importjava.util.Date
           java.util.Calendar))
 
; 类名字后加个”."用来创建一个对象
(Date.) ; <a dateobject>
 
; 使用. 来调用方法. 或者使用“.方法名"简写的方式
(. (Date.) getTime) ; <a timestamp>
(.getTime (Date.)) ; 和上面一样哦
 
; 使用/ 来调用静态方法
(System/currentTimeMillis) ; <a timestamp> (system is always present)
 
; 使用 doto 来处理可变的类<span style="font-family:宋体;">,所有的函数始终用最初的那个对象值,最后还是返回最初的那个对象</span>  (importjava.util.Calendar)
(doto (Calendar/getInstance)
  (.set2000 1 1 0 0 0)
  .getTime) ; => A Date. setto 2000-01-01 00:00:00
0 0