September 21, 2019
By: Kevin

十五个很酷的函数

  1. into
  2. mapcat
  3. mapv
  4. fnil
  5. partial
  6. comp
  7. juxt
  8. iterate
  9. cycle
  10. repeat
  11. repeatedly
  12. constantly
  13. get-in
  14. assoc-in
  15. update-in

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-inassoc-in很像,不过它提供了一个函数,修改当前值。

(update-in zhaoyu [:age] inc)
Tags: clojure clojurescript