我刚读完《The Joy of Clojure》的第 5 章 - “集合类型”,有点混乱(即该书的下一个版本需要复习)。在第 5 章第 86 页,有一张我不太满意的表格:
所以这是我的看法(经过一个月的反思后回到这里,完全更新了)。
收藏
它是一个“事物”,是其他事物的集合。
这是基于函数coll?。
-
coll? 函数可用于测试。
- 相反,
coll? 返回 true 的任何东西都是集合。
coll? 文档字符串说:
如果 x 实现 IPersistentCollection,则返回 true
作为集合的事物被分为三个独立的类。不同类别的事物永远不会相等。
-
地图 使用
(map? foo) 进行测试
- 地图(行为略有不同的两个实际实现)
- 已排序的地图。注意:
(sequential? (sorted-map :a 1) ;=> 错误
-
Sets 使用
(set? foo) 进行测试
- 设置
- 排序集。注意:
(sequential? (sorted-set :a :b)) ;=> 错误
-
顺序集合 使用
(sequential? foo) 进行测试
- 列表
- 矢量
- 队列
- 序列:
(sequential? (seq [1 2 3])) ;=> 真
- 惰性序列:
(sequential? (lazy-seq (seq [1 2 3]))) ;=> 真
Java 互操作的东西不在此范围内:
-
(coll? (to-array [1 2 3])) ;=> 错误
-
(map? (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))) ;=> 错误
顺序收集(“链”)
它是一个“事物”,一个集合包含其他事物根据特定的、稳定的顺序。
这是基于函数sequential?。
- 函数
sequential? 可用于对此进行测试。
- 相反,
sequential? 返回 true 的任何内容都是顺序集合。
sequential? 文档字符串说:
如果 coll 实现了 Sequential,则返回 true
注意:“sequential”是形容词!在“The Joy of Clojure”中,形容词被用作名词,这真的非常非常令人困惑:
"Clojure 将每个集合数据类型分为三种类型之一
逻辑类别或分区:顺序、映射和集合。”
应该使用“顺序事物”或“顺序集合”(如上所述),而不是“顺序”。另一方面,在mathematics 中已经存在以下词:“链”、“全序集”、“单序集”、“线性有序集”。 “链条”听起来很棒,但没有人使用这个词。丢人!
《Clojure 之乐》也有这样的说法:
注意基于类型的谓词!
Clojure 包含一些谓词,其名称类似于单词 just
定义。虽然它们不经常使用,但似乎值得
提到它们可能并不完全意味着这里的定义
可能会建议。例如,每个对象是哪个顺序的?返回
true 是一个顺序集合,但对于某些返回 false
也是顺序的[更好:“可以认为是顺序的
集合”]。这是因为实现细节可能是
在未来版本的 Clojure 中有所改进 [也许这已经是
完成了吗?]
序列(也称为“序列抽象”)
这更像是一个概念而不是一个东西:一系列值(因此排序)可能存在也可能不存在(即流)。如果你说一个事物是一个序列,那么那个事物是否也必然是一个 Clojure 集合,甚至是一个序列集合?我想是的。
该顺序集合可能已经完全计算并且完全可用。或者它可能是根据需要生成值的“机器”(通过计算 - 可能以“纯”方式 - 或通过查询外部“不纯”、“口头”来源:键盘、数据库)
序列
这是一个东西:可以被函数处理的东西
first,rest,next,cons(可能还有其他?),即遵循protocolclojure.lang.ISeq(这与“为接口提供实现”的概念大致相同) Java),即系统已经为一对(thing, function-name)注册了函数实现[我当然希望我做对了……]
这是基于函数seq?。
- 函数
seq?可以用来测试这个
- 相反,seq 是
seq? 返回 true 的任何内容。
seq? 的文档字符串:
如果 x 实现 ISeq,则返回 true
first 的文档字符串:
返回集合中的第一项。在其参数上调用 seq。
如果 coll 为 nil,则返回 nil。
rest 的文档字符串:
在第一个之后返回可能为空的项目序列。调用序列
论据。
next 的文档字符串:
返回第一个之后的项目的序列。在其参数上调用 seq。
如果没有更多项目,则返回 nil。
您在 seq 上调用 next 以生成下一个元素和新的 seq。重复直到获得nil。
Clojure 的乐趣 将其称为“用于导航集合的简单 API”并说“seq 是实现 seq API 的任何对象”——如果“API”是“事物”(某种类型的)和对该事物起作用的功能。这取决于 API 概念的适当转变。
关于空seq的特殊情况:
(def empty-seq (rest (seq [:x])))
(type? empty-seq) ;=> clojure.lang.PersistentList$EmptyList
(nil? empty-seq) ;=> false ... empty seq is not nil
(some? empty-seq) ;=> true ("true if x is not nil, false otherwise.")
(first empty-seq) ;=> nil ... first of empty seq is nil ("does not exist"); beware confusing this with a nil in a nonempty list!
(next empty-seq) ;=> nil ... "next" of empty seq is nil
(rest empty-seq) ;=> () ... "rest" of empty seq is the empty seq
(type (rest empty-seq)) ;=> clojure.lang.PersistentList$EmptyList
(seq? (rest empty-seq)) ;=> true
(= (rest empty-seq) empty-seq) ;=> true
(count empty-seq) ;=> 0
(empty? empty-seq) ;=> true
附录
函数seq
如果您将函数 seq 应用于有意义的事物(通常是顺序集合),您将获得一个表示/生成该集合成员的 seq。
文档字符串说:
返回集合的序列。如果集合为空,则返回
零。 (seq nil) 返回 nil。 seq 也适用于字符串,原生 Java
数组(引用类型)和任何实现 Iterable 的对象。
注意 seqs 缓存值,因此 seq 不应该用于任何
Iterable,其迭代器重复返回相同的可变对象。
申请seq后,可能会得到各种实际类的对象:
-
clojure.lang.Cons - 试试(class (seq (map #(* % 2) '( 1 2 3))))
-
clojure.lang.PersistentList
clojure.lang.APersistentMap$KeySeq
clojure.lang.PersistentList$EmptyList
clojure.lang.PersistentHashMap$NodeSeq
clojure.lang.PersistentQueue$Seq
clojure.lang.PersistentVector$ChunkedSeq
如果将seq应用于序列,则返回的事物的实际类可能与传入事物的实际类不同。它仍然是序列。
序列中的“元素”是什么取决于。例如,对于地图,它们是键值对,看起来像 2 元素 vector(但它们的实际类并不是真正的向量)。
函数lazy-seq
创建一个东西来懒惰地生成更多的东西(一个挂起的机器,一个挂起的流,一个thunk)
文档字符串说:
获取返回 ISeq 或 nil 的表达式主体,并产生一个
仅在第一次 seq 时才会调用主体的 Seqable 对象
调用,并将缓存结果并在所有后续 seq 中返回
来电。另请参阅 - 实现了吗?”
关于“函数”和“事物”...和“对象”的注释
在 Clojure 世界中,我喜欢谈论“函数”和“事物”,而不是谈论“对象”,这是一个充满了 Java 特性和其他不良含义的术语。提及对象感觉就像是从底层 Java 世界中冒出来的碎片。
函数和事物有什么区别?
它是流动的!有些东西是纯函数,有些东西是纯东西,有些介于两者之间(可以作为函数使用,具有事物的属性)
特别是,Clojure 允许将关键字(事物)视为函数(在映射中查找值)或将映射(事物)解释为函数或函数的简写(获取键并返回值)的上下文关联到地图中的那个键)
显然,函数就是“first-class citizens”。
它也是上下文的!在某些情况下,函数变成了事物,或者事物变成了函数。
有一些令人讨厌的提及对象......这些是从底层 Java 世界中冒出来的碎片。
出于演示目的,集合图表