我刚刚看完了“The Joy of Clojure”的第 5 章 - “Collection Types”,这有点令人困惑(即该书的下一个版本需要复习)。在第 5 章第 86 页,有一张我不太满意的表格:
所以这是我的看法(经过一个月的反思后回到这里,完全更新了)。
收藏
它是一个“事物”,是其他事物的集合。
这是基于函数的coll?
。
- 该函数
coll?
可用于对此进行测试。
- 相反,任何
coll?
返回 true 的东西都是一个集合。
coll?
文档字符串说:
如果 x 实现,则返回 trueIPersistentCollection
作为集合的事物被分组为三个单独的类。不同类别的事物永远不会平等。
- 地图测试使用
(map? foo)
- 地图(行为略有不同的两个实际实现)
- 排序的地图。注意:
(sequential? (sorted-map :a 1)
;=> 假
- 设置测试使用
(set? foo)
- 放
- 排序集。注意:
(sequential? (sorted-set :a :b))
;=> 假
- 顺序收集测试使用
(sequential? foo)
- 列表
- 向量
- 队列
- 序列:
(sequential? (seq [1 2 3]))
;=> 真
- 惰性序列:
(sequential? (lazy-seq (seq [1 2 3])))
;=> true
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 将每个集合数据类型分类为三个逻辑类别或分区之一:顺序、映射和集合。”
而不是“顺序”,应该使用“顺序事物”或“顺序集合”(如上所用)。另一方面,在数学中已经存在以下词:“链”、“全序集”、“单序集”、“线性有序集”。“链条”听起来很棒,但没有人使用这个词。耻辱!
《Clojure 之乐》也有这样的说法:
当心基于类型的谓词!
Clojure 包含一些谓词,其名称与刚刚定义的单词类似。尽管它们不经常使用,但似乎值得一提的是,它们可能并不完全代表此处的定义所暗示的含义。例如,每个对象是哪个顺序的?返回 true 是一个顺序集合,但对于一些也是顺序的返回 false [更好:“可以被认为是顺序集合”]。这是因为在 Clojure 的未来版本中可能会改进实现细节 [也许这已经完成了?]
序列(也称为“序列抽象”)
这更像是一个概念而不是一个东西:一系列可能存在或可能不存在的值(因此排序)(即流)。如果你说一个事物是一个序列,那么那个事物是否也必然是一个 Clojure 集合,甚至是一个序列集合?我想是这样。
该顺序集合可能已经完全计算并且完全可用。或者它可能是根据需要生成值的“机器”(通过计算 - 可能以“纯”方式 - 或通过查询外部“不纯”、“口头”来源:键盘、数据库)
序列
这是一个东西:可以由函数
first
, rest
, next
, cons
(可能还有其他?)处理的东西,即遵守协议 clojure.lang.ISeq
的东西(这与 Java 中的“为接口提供实现”的概念大致相同),即系统已经为一对(事物,函数名)注册了函数实现[我当然希望我做对了......]
这是基于函数的seq?
。
- 该函数
seq?
可用于对此进行测试
- 相反,seq 是任何
seq?
返回 true 的东西。
文档字符串seq?
:
如果 x 实现 ISeq,则返回 true
文档字符串first
:
返回集合中的第一项。在其参数上调用 seq。如果 coll 为 nil,则返回 nil。
文档字符串rest
:
在第一个之后返回可能为空的项目序列。在其参数上调用 seq。
文档字符串next
:
返回第一个之后的项目的序列。在其参数上调用 seq。如果没有更多项目,则返回 nil。
你调用next
seq 来生成下一个元素和一个新的 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。
文档字符串说:
返回集合的序列。如果集合为空,则返回 nil。(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 的表达式主体,并生成一个 Seqable 对象,该对象仅在第一次调用 seq 时调用主体,并将缓存结果并在所有后续 seq 调用时返回它。又见——实现了?”
关于“功能”和“事物”......和“对象”的注释
在 Clojure 世界中,我喜欢谈论“函数”和“事物”,而不是谈论“对象”,这是一个充满 Java 特性和其他坏处的术语。提及对象感觉就像是从底层 Java 世界中冒出来的碎片。
功能和事物有什么区别?
是流动的!有些东西是纯函数,有些东西是纯东西,有些介于两者之间(可以用作函数并具有事物的属性)
特别是,Clojure 允许将关键字(事物)视为函数(在映射中查找值)或将映射(事物)解释为函数或函数的简写(接受一个键并返回与该键关联的值)的上下文。输入地图)
显然,函数是“一等公民”。
这也是语境!在某些情况下,一个功能变成了一个东西,或者一个东西变成了一个功能。
有一些令人讨厌的提及对象......这些是从底层 Java 世界中冒出来的碎片。
出于演示目的,集合图