Clojure的sequence抽象

来源:Clojure for the brave and true

什么是Programming to Abstractions?

是一种设计函数库的原则,Clojure采用了这种原则.
即把具有相似特征的数据结构抽象成某个概念,比如sequence.之后这些类的数据都可以用同一个函数操作.

类似的东西

Javascript的Lodash库的mapreduce也可以同时接受数组和对象作为参数.

带来的好处

让你写程序时候,专注于这个数据结构能有哪些操作,同时尽可能地忽略它是如何实现的.

map函数例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(defn titleize
[topic]
(str topic " for the Brave and True"))

(map titleize ["Hamsters" "Ragnarok"])
; => ("Hamsters for the Brave and True" "Ragnarok for the Brave and True")

(map titleize '("Empathy" "Decorating"))
; => ("Empathy for the Brave and True" "Decorating for the Brave and True")

(map titleize #{"Elbows" "Soap Carving"})
; => ("Elbows for the Brave and True" "Soap Carving for the Brave and True")

var list = ["Transylvania", "Forks, WA"];
map(list, function (val) { return val + " mapped!"})
// => ["Transylvania mapped!", "Forks, WA mapped!"]

从例子可以看出,Clojure的map函数可以接受多种数据类型,包括List,Vector,Set,Map.map不是基于特定的数据结构定义的,而是基于sequence定义的.

sequence以及三个核心函数

sequence是Clojure对某些数据结构的抽象,即序列结构.sequence有三个核心函数

  1. first: 参数(一个序列)序列的第一个值 参考
  2. rest: 参数(一个序列)除了第一个值构成的序列 参考
  3. cons: 第一个参数(一个值)和第2个参数(一个序列)新组成的序列 参考

只要某种数据结构支持上面三个核心函数,就能把它视为sequence,因此就能对它进行map(和其他基于sequence定义的函数,包括filter,reduce,group-by等)操作.

sequence函数运行机制

当某个sequence函数(比如map)运行时,会把参数通过seq函数转换成类似List的,支持上述sequence核心函数的数据结构.然后再用它进行相关计算.

1
2
3
4
5
6
7
8
9
10
11
(seq '(1 2 3))
; => (1 2 3)

(seq [1 2 3])
; => (1 2 3)

(seq #{1 2 3})
; => (1 2 3)

(seq {:name "Bill Compton" :occupation "Dead mopey guy"})
; => ([:name "Bill Compton"] [:occupation "Dead mopey guy"])

sequence函数举例

参考这里