33

我是一名 Java 程序员,并且是 Clojure 的新手。从不同的地方,我看到序列和集合在不同的情况下使用。但是,我不知道它们之间的确切区别是什么。

对于一些例子:

1) 在 Clojure 的Sequence文档中:

The Seq interface
(first coll)
  Returns the first item in the collection. 
  Calls seq on its argument. If coll is nil, returns nil.
(rest coll)
  Returns a sequence of the items after the first. Calls seq on its argument. 
  If there are no more items, returns a logical sequence for which seq returns nil.
(cons item seq)
  Returns a new seq where item is the first element and seq is the rest.

如您所见,在描述 Seq 接口时,前两个函数(first/rest)使用coll似乎表明这是一个集合,而cons函数使用seq似乎表明这是一个序列。

2)有一些函数被调用coll?seq?可以用来测试一个值是一个集合还是一个序列。很明显,收藏和顺序是不同的。

3)在 Clojure 关于“集合”的文档中,据说:

因为集合支持 seq 函数,所以所有序列函数都可以与任何集合一起使用

这是否意味着所有集合都是序列?

(coll? [1 2 3]) ; => true 
(seq? [1 2 3]) ; => false

上面的代码告诉我情况并非如此,因为它[1 2 3]是一个集合但不是一个序列。

我认为这对于 Clojure 来说是一个非常基本的问题,但我无法找到一个地方清楚地解释它们的区别是什么以及在不同情况下应该使用哪个。任何评论表示赞赏。

4

6 回答 6

24

任何支持核心firstrest功能的对象都是sequence.

seq许多对象都满足这个接口,并且每个 Clojure 集合都提供至少一种 seq 对象,用于使用该函数遍历其内容。

所以:

user> (seq [1 2 3])
    (1 2 3)

map您也可以从 a 创建一个序列对象

user> (seq {:a 1 :b 2})
    ([:a 1] [:b 2])

这就是为什么您可以使用filter, map,formaps sets

因此,您可以将许多类似集合的对象视为序列。

这也是为什么许多序列处理函数(例如在输入上filter调用)的原因seq

 (defn filter
  "Returns a lazy sequence of the items in coll for which
  (pred item) returns true. pred must be free of side-effects."
  {:added "1.0"
   :static true}
  ([pred coll]
   (lazy-seq
      (when-let [s (seq coll)]

如果你打电话(filter pred 5)

  Don't know how to create ISeq from: java.lang.Long
                  RT.java:505 clojure.lang.RT.seqFrom
                  RT.java:486 clojure.lang.RT.seq
                 core.clj:133 clojure.core/seq
                core.clj:2523 clojure.core/filter[fn]

您会看到seq调用是这个对象的序列验证。

如果你想更深入的话,大部分内容都在Clojure 的第 5 章中。

于 2013-11-08T03:08:43.887 回答
20

这里有几点有助于理解collectionsequence之间的区别。

  1. “集合”和“序列”是抽象,而不是可以从给定值确定的属性。

  2. 集合是价值的袋子。

  3. 序列是一种数据结构(集合的子集),预计以顺序(线性)方式访问。

下图最能说明它们之间的关系:

在此处输入图像描述

你可以在这里阅读更多关于它的信息。

于 2017-03-15T16:35:18.553 回答
16

每个序列都是一个集合,但不是每个集合都是一个序列。

seq函数可以将集合转换为序列。例如,对于地图,您将获得其条目的列表。不过,该条目列表与地图本身不同。

于 2013-11-08T15:44:15.513 回答
15

Clojure 中,作者以一种非常容易理解的方式总结了这一点:

集合抽象与序列抽象密切相关。Clojure 的所有核心数据结构——向量、映射、列表和集合——都参与了这两种抽象。

抽象的不同之处在于,序列抽象是“关于”单独对成员进行操作,而集合抽象是“关于”整个数据结构。例如,集合函数countempty?和 与every?任何单个元素无关;它们是关于整体的。

于 2014-11-22T21:36:53.017 回答
5

我刚刚看完了“The Joy of Clojure”的第 5 章 - “Collection Types”,这有点令人困惑(即该书的下一个版本需要复习)。在第 5 章第 86 页,有一张我不太满意的表格:

表 5.1 来自 Clojure 的喜悦,第 2 版。

所以这是我的看法(经过一个月的反思后回到这里,完全更新了)。

收藏

它是一个“事物”,是其他事物的集合。

这是基于函数的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。

你调用nextseq 来生成下一个元素和一个新的 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 世界中冒出来的碎片。

出于演示目的,集合图

Clojure 中的集合

于 2019-05-16T15:07:40.337 回答
3

对于seq?

如果 x 实现 ISeq,则返回 true

对于coll?

如果 x 实现 IPersistentCollection,则返回 true

而且我发现ISeq 接口从 Clojure 源代码中的 IPersistentCollection 扩展而来,所以正如 Rörd 所说,每个序列都是一个集合。

于 2013-11-10T07:46:13.163 回答