本来这应该放在递归之前的,不过没什么影响,反而对闭包的理解有帮助。把函数作为第一等公民的clojure,自然会给函数提供吊炸天的功能,使其变得吊炸天,写出更吊炸天的闭包!例如串行调用的组合函数、偏函数和柯里化、以及高阶函数等等。


  • 函数的结果传给下一个函数

有时候我们一个数据,需要依次传给一个函数处理后,传递给另外一个函数继续处理,多次传递形成函数链。

假如在一堆没有换行符的字符串中找某个人名以及他的年龄,可能需要这样的函数链。

1
2
3
4
5
6
7
;为了方便,假如只找第一个
(defn find-name&age [names] (vec (.split
(first
(re-seq #"\w*-\d*" names))
"-")))
(find-name&age "tom-42tim-24jim-12");=[tom 42]
;上面find-name&age [names]实在写得难看,一条链下来的函数多得要死不说,单单圆括号就实在让人生畏。

clojure为我们提供了一个可以省去那么多没必要圆括号的函数-comp!!

1
2
;comp从右到左依次接受上一个函数的返回值,最后一个函数则接受comp外的实参
((comp vec #(.split % "-") first re-seq) #"\w*-\d*" "tom-42tim-24jim-12");=[tom 42]

  • 实在不想每次都传那么多个参数!

假如你的代码里总是需要(+ 101 1 2 ? ? ?)这样的函数的,偏函数可以让你传入某些固定的实参并返回一个需要传入剩余参数的新函数。

1
2
3
4
;传入101 1 2三个实参
(def my_+ (partial + 101 1 2))
;传入剩余参数
(my_+ 1 2 3 4);=114

说到偏函数,自然会想到柯里化,然而。.

1
2
3
4
(defn rematch [rex]
#(re-seq rex %))
;括号没法省略,柯里化?算了吧!可以用变参就用变参吧!
((rematch #"\w*-\d*") "tom-42tim-24jim-12")

  • 传入或返回一个函数

函数作为实参传入另外一个函数,后者则是一个高阶函数。clojure提供了许多高阶函数,例如applymapfilter等。

1
2
3
4
(map + [1 2 3 4] [1 2 3]);=(2 4 6)
(apply + [1 2 3 4]);=10
;匿名函数是高阶函数的一个重要组成部分,临时作为实参,尤其作为返回值。
(map #(name %) [:a :b :c]);=("a" "b" "c")

上面的一个例子rematch [rex]就是一个把函数作为返回值的高阶函数。

  • 绝妙的闭包

在之前出现过的例子,有不少已经体现了闭包这个概念。

闭包什么意思呢?简单且不严谨的说,就是返回一个函数,然后这个函数可随意访问定义它的上下文的局部量。A()定义并返回B(),且B()可以返回A()中声明的局部量,那么调用A()时,得到一个闭包函数。偏函数和柯里化是一种闭包。

1
2
3
4
5
6
;函数中定义并返回一个匿名函数
(defn rematch2 [content]
(fn [rex]
;匿名函数可以随意返回上下文(即rematch2内)的局部量content
(re-seq rex (str content "///0099ff"))))
((rematch2 "tom-42tim-24jim-12") #"\d+")