Clojure中序列和集合之间的区别是什么

nyb*_*bon 31 collections abstraction clojure

我是一名Java程序员,也是Clojure的新手.从不同的地方,我看到序列和集合在不同的情况下使用.但是,我不知道他们之间究竟有什么区别.

举个例子:

1)在Clojure的序列文档中:

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.
Run Code Online (Sandbox Code Playgroud)

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

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

3)在Clojure关于" 收藏 " 的文件中,有人说:

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

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

(coll? [1 2 3]) ; => true 
(seq? [1 2 3]) ; => false
Run Code Online (Sandbox Code Playgroud)

上面的代码告诉我它不是这种情况,因为它[1 2 3]是一个集合,但不是一个序列.

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

Gui*_*ler 22

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

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

所以:

user> (seq [1 2 3])
    (1 2 3)
Run Code Online (Sandbox Code Playgroud)

你可以从创建序列对象map

user> (seq {:a 1 :b 2})
    ([:a 1] [:b 2])
Run Code Online (Sandbox Code Playgroud)

这就是为什么你可以使用filter,map,for等对maps 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)]
Run Code Online (Sandbox Code Playgroud)

如果你打电话 (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]
Run Code Online (Sandbox Code Playgroud)

您看到该seq调用是此对象的序列验证.

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


Rör*_*örd 16

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

seq功能可以将集合转换为序列.例如,对于地图,您将获得其条目列表.但是,条目列表与地图本身不同.


Ada*_*old 13

Clojure中,为了勇敢和真实,作者以一种真正可以理解的方式对其进行了总结:

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

抽象的不同之处在于序列抽象是"约"对成员的单独操作,而集合抽象是"关于"整个数据结构.例如,集合函数count,empty?并且every?不是关于任何单个元素; 他们是关于整体的.


div*_*yum 11

以下几点有助于理解集合序列之间的区别.

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

  2. 收藏品是价值包.

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

下图最好地描述了它们之间的关系:

在此输入图像描述

你可以在这里阅读更多相关信息.


Dav*_*fer 7

我刚刚读完《The Joy of Clojure》的第 5 章 - “集合类型”,这有点令人困惑(即该书的下一个版本需要回顾)。在第 5 章第 86 页,有一个我不太满意的表格:

\n\n

表 5.1 来自《Clojure 的乐趣》,第二版。

\n\n

这是我的看法(经过一个月的反思后完全更新)。

\n\n

收藏

\n\n

它是一个“东西”,是其他东西的集合。

\n\n

这是基于函数的coll?

\n\n
    \n
  • 该函数coll?可用于对此进行测试。
  • \n
  • 相反,任何coll?返回 true 的东西都是集合。
  • \n
\n\n

文档coll?字符串说:

\n\n
\n

如果 x 实现则返回 trueIPersistentCollection

\n
\n\n

事物是分为三个不同类的集合。不同阶级的事物永远不平等。

\n\n
    \n
  • 使用地图(map? foo)测试\n\n
      \n
    • 地图(行为略有不同的两个实际实现)
    • \n
    • 排序的地图。注意:(sequential? (sorted-map :a 1);=> 错误
    • \n
  • \n
  • 使用(set? foo)\n\n设置测试
      \n
    • \n
    • 已排序的集合。注意:(sequential? (sorted-set :a :b));=> 错误
    • \n
  • \n
  • 顺序集合使用(sequential? foo)\n\n进行测试
      \n
    • 列表
    • \n
    • 向量
    • \n
    • 队列
    • \n
    • 序列:(sequential? (seq [1 2 3]));=> true
    • \n
    • 惰性序列:(sequential? (lazy-seq (seq [1 2 3])));=> true
    • \n
  • \n
\n\n

Java 互操作的内容不在此列:

\n\n
    \n
  • (coll? (to-array [1 2 3]));=> 假
  • \n
  • (map? (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2)));=> 假
  • \n
\n\n

顺序收集(“链”)

\n\n

它是一个“东西”,一个按照特定的、稳定的顺序保存其他东西的集合。

\n\n

这是基于函数的sequential?

\n\n
    \n
  • 该函数sequential?可用于对此进行测试。
  • \n
  • 相反,任何sequential?返回 true 的内容都是顺序集合。
  • \n
\n\n

文档sequential?字符串说:

\n\n
\n

如果 coll 实现 Sequential,则返回 true

\n
\n\n

注意:“顺序”是形容词!在《The Joy of Clojure》中,形容词被用作名词,这真的非常非常令人困惑:

\n\n
\n

“Clojure 将每个集合数据类型分为三个逻辑类别或分区之一:顺序、映射和集合。”

\n
\n\n

应该使用“顺序事物”或“顺序集合”(如上面所使用的),而不是“顺序”。另一方面,在数学中已经存在以下术语:“链”、“全序集”、“单序集”、“线性有序集”。“链条”听起来很棒,但没有人使用这个词。耻辱!

\n\n

《Joy of Clojure》也有这样的说法:

\n\n
\n

当心基于类型的谓词!

\n\n

Clojure 包含一些谓词,其名称类似于刚刚定义的单词。尽管它们\xe2\x80\x99 不经常使用,但似乎值得一提的是,它们可能并不完全符合此处定义的含义。例如,每个对象的顺序是什么?returns\n true 是一个顺序集合,但对于某些也是顺序的集合返回 false [更好:“可以被视为顺序\n 集合”]。这是因为 Clojure 的未来版本可能会改进实现细节 [也许这已经完成了?]

\n
\n\n

序列(也称为“序列抽象”)

\n\n

这更多的是一个概念而不是一个事物:一系列可能存在也可能不存在的值(因此排序)(即流)。如果说一个事物是一个序列,那么这个事物是否也一定是一个 Clojure 集合,甚至是一个顺序集合?我想是这样。

\n\n

该顺序集合可能已被完全计算并且完全可用。或者它可能是一台根据需要生成值的“机器”(通过计算 - 可能以“纯粹”方式 - 或通过查询外部“不纯”、“神谕”来源:键盘、数据库)

\n\n

序列

\n\n

这是一个东西:可以被函数处理的东西\n first, rest, next, cons(可能还有其他?),即遵守协议 clojure.lang.ISeq的东西(这与Java中的“为接口提供实现”的概念大致相同) ,即系统已经注册了一对(事物,函数名称)的函数实现[我当然希望我得到了正确的结果......]

\n\n

这是基于函数的seq?

\n\n
    \n
  • 该函数seq?可用于测试这一点
  • \n
  • 相反,seq 是seq?返回 true 的任何内容。
  • \n
\n\n

文档字符串seq?

\n\n
\n

如果 x 实现 ISeq,则返回 true

\n
\n\n

文档字符串first

\n\n
\n

返回集合中的第一项。对其参数调用 seq。\n 如果 coll 为 nil,则返回 nil。

\n
\n\n

文档字符串rest

\n\n
\n

返回第一个之后的可能为空的项目序列。在其参数上调用 seq\n。

\n
\n\n

文档字符串next

\n\n
\n

返回第一个之后的项目的序列。对其参数调用 seq。\n 如果没有更多项目,则返回 nil。

\n
\n\n

您调用nextseq 来生成下一个元素和一个新的 seq。重复直到nil获得。

\n\n

Clojure 的乐趣称其为“用于导航集合的简单 API”,并表示“seq 是实现 seq API 的任何对象”——如果“API”是“事物”(某种类型)的整体,则这是正确的以及对该事物起作用的功能。这取决于 API 概念的适当转变。

\n\n

关于空 seq特殊情况的注释:

\n\n
(def empty-seq (rest (seq [:x])))\n\n(type? empty-seq)                 ;=> clojure.lang.PersistentList$EmptyList\n\n(nil? empty-seq)                  ;=> false ... empty seq is not nil\n(some? empty-seq)                 ;=> true ("true if x is not nil, false otherwise.")\n\n(first empty-seq)                 ;=> nil   ... first of empty seq is nil ("does not exist"); beware confusing this with a nil in a nonempty list!\n(next empty-seq)                  ;=> nil   ... "next" of empty seq is nil\n(rest empty-seq)                  ;=> ()    ... "rest" of empty seq is the empty seq\n   (type (rest empty-seq))        ;=> clojure.lang.PersistentList$EmptyList\n   (seq? (rest empty-seq))        ;=> true\n   (= (rest empty-seq) empty-seq) ;=> true\n\n(count empty-seq)                 ;=> 0\n(empty? empty-seq)                ;=> true\n
Run Code Online (Sandbox Code Playgroud)\n\n

附加物

\n\n

功能seq

\n\n

如果将该函数应用于seq有意义的事物(通常是顺序集合),您将得到一个表示/生成该集合的成员的 seq。

\n\n

文档字符串说:

\n\n
\n

返回集合上的 seq。如果集合为空,则返回 nil。(seq nil) 返回 nil。seq 还适用于字符串、本机 Java\n 数组(引用类型)以及任何实现 Iterable 的对象。\n 请注意,seq 会缓存值,因此 seq 不应用于任何迭代器重复返回相同可变对象的 Iterable\n 。

\n
\n\n

应用后seq,你可能会得到各种实际类的对象:

\n\n
    \n
  • clojure.lang.Cons- 尝试(class (seq (map #(* % 2) \'( 1 2 3))))
  • \n
  • clojure.lang.PersistentList
  • \n
  • clojure.lang.APersistentMap$KeySeq
  • \n
  • clojure.lang.PersistentList$EmptyList
  • \n
  • clojure.lang.PersistentHashMap$NodeSeq
  • \n
  • clojure.lang.PersistentQueue$Seq
  • \n
  • clojure.lang.PersistentVector$ChunkedSeq
  • \n
\n\n

如果应用seq到序列,返回的事物的实际类可能与传入的事物的实际类不同。它仍然是一个序列。

\n\n

序列中的“元素”是什么取决于。例如,对于映射,它们是看起来像 2 元素的键值对vector(但它们的实际类并不是真正的向量)。

\n\n

功能lazy-seq

\n\n

创建一个东西来延迟生成更多东西(一个暂停的机器,一个暂停的流,一个thunk

\n\n

文档字符串说:

\n\n
\n

获取返回 ISeq 或 nil 的表达式主体,并生成一个 Seqable 对象,该对象仅在第一次调用 seq 时调用主体,并缓存结果并在所有后续 seq 调用中返回它。另见——意识到了吗?”

\n
\n\n

关于“函数”和“事物”......以及“对象”的注释

\n\n

在 Clojure 宇宙中,我喜欢谈论“函数”和“事物”,但不喜欢谈论“对象”,因为“对象”是一个充满 Java 特性和其他缺点的术语。提到对象感觉就像是从底层 Java 宇宙中冒出来的碎片。

\n\n

功能和事物有什么区别?

\n\n

它是流体!有些东西是纯函数,有些东西是纯函数,有些介于两者之间(可以用作函数并具有事物的属性)

\n\n

特别是,Clojure 允许将关键字(事物)视为函数(在映射中查找值)或将映射(事物)解释为函数或函数的简写(它采用一个键并返回与该键关联的值)的上下文。地图上的键)

\n\n

显然,功能就是事物,因为它们是“一等公民””。

\n\n

这也是有上下文的!在某些情况下,功能变成了事物,或者事物变成了功能。

\n\n

有一些令人讨厌的对象提及......这些是从底层 Java 宇宙中冒出来的碎片。

\n\n

出于演示目的,集合图

\n\n

Clojure 中的集合

\n