收藏的学习地址:
Leiningen中文教程
luminusweb官方文档
Clojure常用包
leiningen API
clojure常用库列表
clojure技术书籍列表
clojure语法中文教程
clojure函数式编程
学习中命令行里敲过的
(+ 11 11 1111)
(/ 1 2)
(doc /)
(Take 5 (repeat 10))
(1 2 3) => error
'(1 2 3) => (1 2 3)
(doc if)
(def a-array [1 2 3])
(def b-array [1 2 3])
(idertical? a-array b-array)
(def a-list (1 2 3))
(def b-list [1 2 3])
(type a-list)
(type b-list)
(conj a-list 9)
(conj b-list 9)
(def my-add (fn [x y] (+ x y)))
(my-add 4 2)
`#{1 2 3}`
(type #{1 2 3})
(type {:a a})
(filter even? [1 2 3 ])
(map inc [1 2 3])
常用的一些函数的使用体会
- map
# map 遍历list,格式化list里map的key对应的value
(require '[clj-time.format :as f])
=> nil
(def custom-formatter (f/formatter "yyyyMMdd"))
=> #'user/custom-formatter
(defn convert-format [d] (f/unparse custom-formatter (f/parse (f/formatter :date-hour-minute-second ) d)))
=> #'user/convert-format
(map (fn [m] (assoc m :create-time (convert-format (:create-time m))) )
[{:id 1
:create-time "2019-04-17T14:47:24"}
{
:id 2
:create-time "2019-04-17T14:47:44"}])
=> ({:id 1, :create-time "20190417"} {:id 2, :create-time "20190417"})
# 在写一个
(map (fn [m] (assoc m
:office-name (format "%s-%s" (:office-id m) (:office-name m))))
[{
:office-id "1",
:office-name "耳鼻喉科",
:hospital-id "1",
:create-time "2019-04-17T14:47:24",
:deleted 0
},
{
:office-id "2",
:office-name "变态反应科",
:hospital-id "1",
:create-time "2019-04-17T14:47:44",
:deleted 0
}])
=>
({:office-id "1", :office-name "1-耳鼻喉科", :hospital-id "1", :create-time "2019-04-17T14:47:24", :deleted 0}
{:office-id "2", :office-name "2-变态反应科", :hospital-id "1", :create-time "2019-04-17T14:47:44", :deleted 0})
map的解构
user> (def my-map {:a 1 :b 2 :c 3})
#'user/my-map
user> (let [{x :a y :c} my-map]
(println ":a val:" x ", :c val: " y))
:a val: 1 , :c val: 3
nil
user> (let [{:keys [a c]} my-map]
(println ":a val:" a ", :c val: " c))
:a val: 1 , :c val: 3
nil
user> (let [{:strs [foo bar]} {"foo" 1 "bar" 2}]
(println "FOO:" foo "BAR: " bar ))
FOO: 1 BAR: 2
nil
user> (let [{:syms [foo bar]} {'foo 1 'bar 2}]
(println "FOO:" foo "BAR:" bar))
FOO: 1 BAR: 2
nil
user> (def data
{:foo {:a 1
:b 2}
:bar {:a 10
:b 20}})
(let [{{:keys [a b]} :foo
{a2 :a b2 :b} :bar} data]
[a b a2 b2])
#'user/data[1 2 10 20]
user> (def my-map {:a 3 :b 4})
(let [{a :a
b :b
:keys [c d]
:or {a 1
c 2}} my-map]
(println a b c d))
#'user/my-map3 4 2 nil
2、date、timestamp、string
(import '[java.text SimpleDateFormat])
(import '[java.sql Timestamp])
;; timelong is the long int time seconds
(defn timestamp2date [timelong]
(.toString (Timestamp. timelong)))
;; date is date string 04/16/2012
(defn date2timestamp [date]
(.getTime (.parse (SimpleDateFormat. "MM/dd/yyyy") date)))
3、conj
4、seq
5、when-let
6、comp
就是对参数从右到左组合执行所有函数,如下面的函数:
((comp f1 f2 .. fn) arg1 arg2 .. argn)
可以转变为:
(f1 (f2 (.. (fn arg1 arg2 .. argn))))
举例
((comp str +) 8 8 8)
;;=> "24"
(filter (comp not zero?) [0 1 0 2 0 3 0 4])
;;=> (1 2 3 4)
(map
(comp - (partial + 3) (partial * 2))
[1 2 3 4])
;;=> (-5 -7 -9 -11)
7、partial
形如:((partial f arg1 arg2 .. argn) arga argb .. argz)
就是执行:
(f arg1 arg2 .. argn arga argb .. argz)
注意:偏函数的第一个参数是一个函数,后面至少有1个其他参数
partial函数称为“偏函数”或者“部分完整函数”,因为它是不完整的,定义也用def而不是defn。
user=> (def hundred-times (partial * 100))
#'user/hundred-times
user=> (hundred-times 5)
500
user=> (hundred-times 4 5 6)
12000
8、reduce
9、apply
函数间对比使用
1、assoc vs update-in vs assoc-in
说法不一,详情参考Assoc-vs-Update/
assoc,只允许更新数据结构的第一层。处理数据的性能最高,几乎是assoc-in的两倍。
assoc-in 和update-in 可以使用路径表达式改变数据结构的内层。
如果新的值不依赖旧值,assoc-in就可以满足需求,不需要使用update-in;在有依赖关系时,使用update-in
(defrecord Person [fname lname address])
(defrecourd Address [street city state zip])
(def stu (Person. "Stu" "Halloway" (Address. "200 N Mangum" "Durham" "NC" 27707)))
(assoc stu :fname "Stuart")
(upate-in stu [:address :zip] inc)
有用的代码段
1、记录接口响应时间middleware
(defn record-response-time [handler]
(fn [req]
(let [start-date (System/currentTimeMillis)]
(handler req)
(let [res-time (- (System/currentTimeMillis) start-date)]
(println (format "%s took %d ms" (:uri req) res-time))))))
需要注意的是 record-response-time 需要放在 middleware 最外层,这样它才能纪录一个请求经过所有 middleware + handler 处理的时间。
2、移除map中值为nil的key
参考clojure nil? - Remove nil values from a map?
我使用的两个
(into {} (filter #(not (nil? (val %))) {:a true :b false :c nil}))
(into {} (remove #(nil? (val %)) {:a true :b false :c nil}))
3、判断字符串包含
参考:https://stackoverflow.com/questions/26386766/check-if-string-contains-substring-in-clojure
使用(.contains "The Band Named Isis" "Isis")
或者(clojure.string/includes? "abc" "ab")
The easiest way is to use the contains method from java.lang.String:
(.contains "The Band Named Isis" "Isis")
=> true
You can also do it with regular expressions, e.g.
(re-find #"Isis" "The Band Named Isis")
=> "Isis"
(re-find #"Osiris" "The Band Named Isis")
=> nil
If you your result to be true or false, you can wrap it in boolean:
(boolean (re-find #"Osiris" "The Band Named Isis"))
=> false
(use '[clojure.string :as s])
(s/includes? "abc" "ab") ; true
(s/includes? "abc" "cd") ; false
以下规范来自:https://blog.csdn.net/zdplife/article/details/51534182
4、切割或者拼接vector
最后想要谈到的就是函数效率问题,在transient(http://blog.csdn.net/zdplife/article/details/52138512)那篇文章中介绍了,clojure中有些函数使用该数据特性可以提高效率,所以我们也尽量选择使用一些利用了transient特性的函数,尤其在效率要求比较高的工程中:
;;因为pop函数使用了transient数据结构,所以尽量使用第二种写法:
(vec (take 2 [1 2 3]))
;;=> [1 2]
(pop [1 2 3])
;;=> [1 2]
;;因为into函数使用了transient数据结构,所以尽量使用第二种写法:
(vec (concat [1 2] [3 4]))
;;=> [1 2 3 4]
(into [1 2] [3 4])
;;=> [1 2 3 4]
5、vector和list的操作:
给seq增加元素:conj和cons
cons函数不管是向什么类型的序列中插入元素都会放在序列的头上,而conj函数在插入元素时,会考虑到原有序列类型的插入效率,因为list支持头部高速插入,所以会放在list头部,而如果是vector则会放到其尾部删除seq里的元素
删除序列中的元素常用的函数有rest,pop,subvec,其中rest函数类似cons是序列操作函数,返回的是一个序列类型,而pop函数会保持原有数据类型不变,subvec函数对vector操作,返回原有类型,它创建的向量与原来的内部结构一样,非常有效,执行时间为常数。-
从vector中获取元素
修改元素用update或者assoc
6、函数体内偏好使用 pre 函数与 post 条件来检查。
(def bar inc)
;; good
(defn foo [x]
{:pre [(pos? x)]}
(bar x))
;; bad
(defn foo [x]
(if (pos? x)
(bar x)
(throw (IllegalArgumentException "x must be a positive number!")))
7、使用 seq 作为终止条件来测试序列是否为空(这个技巧有时候称为 nil punning)。
;; good
(defn print-seq [s]
(when (seq s)
(prn (first s))
(recur (rest s))))
;; bad
(defn print-seq [s]
(when-not (empty? s)
(prn (first s))
(recur (rest s))))
8、巧用补足函数complement
(map (complement even?) '(1 2 3 4))
;; return...
;; (true false true false)
;; see also ...
(map even? '(1 2 3 4))
;; (false true false true)
;; WORNING
;; This function returns ERROR!!!
(map (not even?) '(1 2 3 4))
(map #(not (even? %)) '(1 2 3 4)) ; This works
9、comp,partial让代码更简洁
;; good
(map #(capitalize (trim %)) ["top " " test "])
;; better
(map (comp capitalize trim) ["top " " test "])
;; good
(map #(+ 5 %) (range 1 10))
;; (arguably) better
(map (partial + 5) (range 1 10))
10、用condp代替cond,当表达式是编译期间常量,应该用case
;; good
(cond
(= x 10) :ten
(= x 20) :twenty
(= x 30) :forty
:else :dunno)
;; better
(condp = x
10 :ten
20 :twenty
30 :forty
:dunno)
;; best
(case x
10 :ten
20 :twenty
30 :forty
:dunno)
11、使用包装好的java interop
;;; object creation
;; good
(java.util.ArrayList. 100)
;; bad
(new java.util.ArrayList 100)
;;; static method invocation
;; good
(Math/pow 2 10)
;; bad
(. Math pow 2 10)
;;; instance method invocation
;; good
(.substring "hello" 1 3)
;; bad
(. "hello" substring 1 3)
;;; static field access
;; good
Integer/MAX_VALUE
;; bad
(. Integer MAX_VALUE)
;;; instance field access
;; good
(.someField some-object)
;; bad
(. some-object some-field)
12、利用关键字可以当作复合类型的函数这个事实。
((juxt :a :b) {:a "ala" :b "bala"})
;;["ala" "bala"]
13、vector数组转字符串
(clojure.string/join "," ["123" "234" "234"])
=>"123,234,234"
反过来就得用clojure.string.split这个函数了
(clojure.string/split "Clojure is awesome!" #" ")
["Clojure" "is" "awesome!"]