我刚刚阅读了《Clojure之乐》第五章“集合类型”,但有些地方令人困惑(即该书的下一版需要审查)。在第五章的第86页,有一张表格,我对它不是完全满意:
![Table 5.1 from the Joy of Clojure, 2nd ed.](https://istack.dev59.com/4ZQS2.webp)
以下是我的观点(在反思一个月后完全更新)。
集合
它是一个“东西”,是其他东西的集合。
这是基于函数coll?
。
- 函数
coll?
可用于测试此功能。
- 相反,任何
coll?
返回true的内容都是集合。
coll?
Docstring说:
如果x实现了IPersistentCollection
,则返回true
被归为三个不同类别的集合。不同类别中的物品永远不会相等。
- Maps测试使用
(map? foo)
- Map(两个实际实现,行为略有不同)
- 排序的映射。注意:
(sequential? (sorted-map :a 1))
;=> false
- Sets测试使用
(set? foo)
- Set
- 排序的集合。注意:
(sequential? (sorted-set :a :b))
;=> false
- Sequential collections测试使用
(sequential? foo)
- List
- Vector
- Queue
- Seq:
(sequential? (seq [1 2 3]))
;=> true
- Lazy-Seq:
(sequential? (lazy-seq (seq [1 2 3])))
;=> true
Java交互部分不在此范围内:
(coll? (to-array [1 2 3]))
;=> false
(map? (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2)))
;=> false
序列集合(一种“链”)
它是一个“东西”,一个按照特定、稳定的顺序保存其他东西的集合。
这基于函数sequential?
。
- 函数
sequential?
可用于测试此内容。
- 相反,任何返回true的
sequential?
都是一个序列集合。
sequential?
文档字符串如下:
如果coll实现了Sequential,则返回true
注意:“sequential”是一个形容词!在《Clojure之乐》中,形容词被用作名词,这真的非常令人困惑:
“Clojure将每个集合数据类型分为三个逻辑类别或分区:序列集合、映射和集合。”
代替“sequential”,应该使用“sequential thing”或“sequential collection”(如上所述)。另一方面,在
mathematics中已经存在以下单词:“chain”,“totally ordered set”,“simply ordered set”,“linearly ordered set”。 “chain”听起来很好,但没有人使用这个词。可惜!
《Clojure之乐》也有这样的说法:
注意基于类型的谓词!Clojure包括一些谓词,其名称类似于刚才定义的单词。虽然它们并不经常使用,但似乎值得提到的是,它们可能并不完全意味着这里可能建议的定义。例如,对于每个使sequential?返回true的对象,都是一个sequential collection,但对于一些也是sequential的对象,它返回false [better: "that can be considered sequential collections"]。这是由于实现细节,可能在未来版本的Clojure中得到改进[也许已经完成了?]
序列(也称为“序列抽象”)
这更像是一个概念而非一件事物:一系列值(因此有序),可能存在也可能不存在(即流)。如果你说一个东西是一个序列,那么这个东西是否必须是一个Clojure集合,甚至是一个连续的集合?我想是的。
这个连续的集合可以完全计算并完全可用。或者它可以是一个“机器”,根据需要生成值(通过计算 - 可能以“纯”的方式 - 或通过查询外部“不纯”的、“神谕”的来源:键盘、数据库)。
seq
这是一个东西:可以由函数
first
、
rest
、
next
、
cons
(可能还有其他函数?)处理的东西,即遵循
protocolclojure.lang.ISeq
(与Java中“为接口提供实现”的概念相同)的系统已经注册了函数实现对于一对
(thing,function-name) [我真心希望我理解正确...]。
这基于函数
seq?
。
- 函数
seq?
可用于测试此内容
- 反之,序列是任何
seq?
返回true的东西。
seq?
的文档字符串:
如果x实现ISeq,则返回true
first
的文档字符串:
返回集合中的第一个项目。在其参数上调用seq。
如果coll为nil,则返回nil。
rest
的文档字符串:
返回第一个后面的可能为空的项。在其参数上调用seq。
next
的文档字符串:
返回第一个后面的项的seq。在其参数上调用seq。
如果没有更多的项,则返回nil。
您可以在序列上调用next
以生成下一个元素和新序列。重复此过程,直到获取nil
。
Joy of Clojure将其称为“用于导航集合的简单API”,并表示“seq是实现seq API的任何对象”-如果“API”是“事物”(某种类型)和在该事物上工作的函数的整体,则这是正确的。这取决于API概念上的适当转变。
关于空序列的特殊情况的说明:
(def empty-seq (rest (seq [:x])))
(type? empty-seq)
(nil? empty-seq)
(some? empty-seq)
(first empty-seq)
(next empty-seq)
(rest empty-seq)
(type (rest empty-seq))
(seq? (rest empty-seq))
(= (rest empty-seq) empty-seq)
(count empty-seq)
(empty? empty-seq)
附录
函数seq
如果你将函数seq
应用于一个有意义的东西(通常是一个序列集合),你会得到一个代表/生成该集合成员的序列。
文档字符串如下:
返回一个集合的序列。如果集合为空,则返回nil。(seq nil)返回nil。seq还适用于字符串、本地Java数组(引用类型)和任何实现Iterable的对象。请注意,seq缓存值,因此不应在其迭代器重复返回相同可变对象的任何Iterable上使用seq。
应用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元素向量的键值对(但它们的实际类并不是真正的向量)。
lazy-seq
函数
创建一个生成更多物品的懒加载的东西(一个暂停的机器,一个暂停的流,一个
thunk)
文档字符串如下:
接受一个返回 ISeq 或 nil 的表达式列表,并产生一个 Seqable 对象,该对象在第一次调用 seq 时仅调用表达式一次,并将结果缓存并在所有后续的 seq 调用中返回。另请参见-realized?"。
关于“函数”和“物品”...以及“对象”的注释
在 Clojure Universe 中,我喜欢谈论“函数”和“物品”,但不是“对象”,因为这个术语充满了 Java-ness 和其他糟糕的东西。提到对象感觉像是从底层的Java universe中突出的碎片。
函数和物品之间有什么区别?
它是流动的!有些东西是纯函数,有些东西是纯物品,有些则介于两者之间(可以用作函数并具有物品的属性)。
特别地,Clojure允许将关键字(things)视为函数(在映射中查找值),或将映射(things)解释为函数,或缩写为函数(它们接受一个键并返回映射中与该键相关联的值)。
显然,函数作为"first-class citizens"也是一种thing。
这也是上下文相关的!在某些情况下,函数变成了一个thing,或者一个thing变成了一个函数。
有关对象的不良提及...这些是从底层Java世界中突出的碎片。
为了演示目的,附上集合的图表
![Collections in Clojure](https://istack.dev59.com/rWhKE.webp)
seq?
并不测试一个“东西”是否是一个“序列”(或者更确切地说,一个“顺序集合”),而是它是否是一个seq
(也就是可以通过first
、next
、rest
操作的东西,严格来说是一个clojure.lang.PersistentVector$ChunkedSeq
)。观察:(def x [1 2])
,然后:(coll? x)
=> true,(sequential? x)
=> true,(seq? x)
=> false,但是!(seq? (seq x))
=> true。 - David Tonhofer