十五个很酷的函数
Clojure核心函数总会给我们带来惊喜,4Clojure练习的时候都有这个体验。 下面这15个函数,总有一个你没怎么用过的吧^^
into
文档:
(doc into)
基本上等同于(reduce conj first-coll second-coll),用于不同类型的collection的互相转化。
比如从vector到map.
(into {} [[:a 1] [:b 2] [:c 3] [:d 4]])
;=> {:a 1, :b 2, :c 3, :d 4}
又比如从list到vector,从map到sorted-map:
;=> [0 1 2 3 4 5 6 7 8 9]
(into [] (range 10))
;=> {:a 1, :b 2, :c 3}
(into (sorted-map) {:b 2 :c 3 :a 1})
mapcat
先map在concat,相当于(concat (map ...))
(defn 乘1-乘2-乘3 [x]
[(* x 1) (* x 2) (* x 3)])
(mapcat 乘1-乘2-乘3 (range 10))
mapv
文档:
(doc mapv)
把map的结果处理成一个vector, 等价于(into [] (map ...))
(mapv #(* % 2) (range 10))
;=> [0 2 4 6 8 10 12 14 16 18]
fnil
文档:
(doc fnil)
fnil是个高阶函数,输入是一个函数,返回是一个新函数,新函数专门处理原来函数参数为nil,或者干脆没有传参的场景。可以理解为为函数增加一个默认参数。
特别注意, fnil最多只支持三个参数.
(require '[goog.string :as s])
(defn favs [age food]
(s/format "我的今年 %d 岁了,我最喜欢吃 %s." age food))
(def favs-with-defaults
(fnil favs 200 "蟠桃"))
;;"我的今年 200 岁了,我最喜欢吃 蟠桃."
(favs-with-defaults )
;;"我的今年 1 岁了,我最喜欢吃 苦瓜."
(favs-with-defaults 1 "苦瓜")
;;"我的今年 1 岁了,我最喜欢吃 蟠桃."
(favs-with-defaults 1 nil)
(favs-with-defaults 1)
;;"我的今年 1 岁了,我最喜欢吃 蟠桃."
(favs-with-defaults 1 "香蕉")
在处理不确定内容的嵌套结构的时候特别有用:
(update-in {} [:user :game :score] (fnil inc 0))
如果没有fnil会发生错误
(update-in {} [:user :game :score] (inc 0))
partial
文档:
(doc partial)
partial是一个高阶函数,接受一个函数和这个函数的部分参数,返回一个新的函数,新函数的参数是剩余的参数。相当于部分参数传入默认值。
(def 加8 (partial + 8))
(加8 10)
comp
文档:
(doc comp)
comp是一个高阶函数,接受一组函数为参数,这些函数能够收尾相接,处理计算结果。
(def concat-and-reverse (comp (partial apply str) reverse concat))
(concat-and-reverse "Hello" "world!")
juxt
文档:
(doc juxt)
juxt 是一个高阶函数,接受一组函数为参数[f1 f2 .. fn],返回一个函数f, 返回的函数接受一个参数e,返回一个vector v: [f1(e) f2(e)... fn(e)]
(def 加减乘除
(juxt inc dec #(* % 2) #(/ % 2)))
(加减乘除 5)
juxt在multimethod下特别有用。
(defmulti 猜水果 (juxt :color :size))
(defmethod 猜水果 [:red :small]
[fruit]
"樱桃")
(defmethod 猜水果 [:green :large]
[fruit]
"大西瓜")
iterate
文档:
(doc iterate)
iterate接受两个参数1.函数f 2. 初始值e, 返回一个懒加载的无穷序列 (e (f e) (f (f e)) ...)
(take 10 (iterate inc 0))
clojure doc上的一个一个例子:
(def lazy-fibs
(map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
(take 10 lazy-fibs)
cycle
文档:
(doc cycle)
cycle接受一个collection,返回一个懒加载的无穷序列,序列的元素来自collection,不断重复循环。
(defn rotations [coll]
(take (count coll) (partition (count coll) 1 (cycle coll))))
(rotations "Hello")
repeat
文档:
(doc repeat)
repeat接受一个element,返回一个懒加载的无穷序列,序列元素全是这个element
(take 5 (repeat \x))
repeat有一个可选参数,可以指定返回的序列的元素数量。
(repeat 5 \x)
(into {} (map vector [55 144 292 344 393] (repeat :ok)))
repeatedly
文档:
(doc repeatedly)
repeatedly首先是个副词,语法上副词是修饰动词的(函数)。所以是个高阶函数,此函数接受 1. 一个函数(此函数不带参数)f 2. 数字 n,代表执行n次f函数。 返回一个结果构成的list。
这个函数在做初始化,或者测试的场景下非常有用。
(repeatedly 5 #(rand-int 500))
注意要区分repeat,它只能返回一个特定值:
(repeat 5 (rand-int 500))
constantly
文档:
(doc constantly)
constantly高阶函数, 接受一个参数x,返回一个函数,此函数无论给定任何输入,都会返回x。
(map (constantly 42) (range 10))
get-in
文档:
(doc constantly)
(def zhaoyu
{:name {:first-name "Kevin"
:last-name "Li"}
:age 35
:hobbies ["编程" "吃水煮肉片" "爬山"]})
(get-in zhaoyu [:name :last-name])
可以在map里取值:
(get-in zhaoyu [:hobbies 2])
可以用于vector:
(get-in zhaoyu [:name :middle-name])
可以指定默认值:
(get-in zhaoyu [:hobbies 3] :没找到)
assoc-in
文档:
(doc assoc-in)
可以用来修改:
(assoc-in zhaoyu [:age] 99)
可以用来增加新的字段:
(assoc-in zhaoyu [:address :city] "青岛")
update-in
文档:
(doc update-in)
update-in和assoc-in很像,不过它提供了一个函数,修改当前值。
(update-in zhaoyu [:age] inc)