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

时间:2013-11-08 02:48:02

标签: collections clojure abstraction

我是一名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函数使用{{1}这似乎表明这是一个序列。

2)有一些名为seqcoll?的函数可用于测试值是集合还是序列。很明显,收集和顺序是不同的。

3)在Clojure关于“Collections”的文档中,有人说:

  

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

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

seq?

上面的代码告诉我情况并非如此,因为(coll? [1 2 3]) ; => true (seq? [1 2 3]) ; => false 是一个集合,但不是一个序列。

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

6 个答案:

答案 0 :(得分:22)

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

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

所以:

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

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

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

这就是为什么您可以在filter map上使用formapssets等等。

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

这就是为什么许多序列处理函数,例如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调用此对象是序列验证。

如果你想深入了解,大部分内容都在Joy of Clojure第5章。

答案 1 :(得分:16)

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

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

答案 2 :(得分:13)

Clojure for the brave and true中,作者以一种可以理解的方式对其进行总结:

  

集合抽象与序列密切相关   抽象。所有Clojure的核心数据结构 - 矢量,地图,   列表和集合 - 参与两个抽象。

     

抽象的不同之处在于序列抽象是"关于"   在集合抽象是单独操作成员   "约"数据结构整体。例如,集合   函数countempty?every?不是关于任何个人的   元件;他们是关于整体的。

答案 3 :(得分:10)

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

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

  2. 收藏品是价值袋。

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

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

    enter image description here

    您可以详细了解here

答案 4 :(得分:3)

seq?

  

如果x实现ISeq

,则返回true

coll?

  

如果x实现IPersistentCollection

,则返回true

我发现ISeq interface来自Clojure源代码中的IPersistentCollection,正如Rörd所说,每个序列都是一个集合。

答案 5 :(得分:1)

我刚刚读完“ Clojure的喜悦”的第5章-“集合类型”,这有点令人困惑(即该书的下一版本需要复习)。这是我的看法:

收藏

这只是“集合”数据类型之一(它们实际上是面向对象的对象,具有某些属性,方法和行为)。为Clojure Universe制作的实例是持久的(即不可变的),但是如果绝对必要,您也可以直接基于Java Collection类创建Collection类型的实例,这些实例是可变的。

在Clojure宇宙中有:

  • 序列(sequential?产生true的序列)
    • 列表
    • 向量
    • 排队
  • 地图
    • 地图(两个实际的实现方式,其行为略有不同)
    • 排序的地图
    • 设置
    • 排序集

出于演示目的,使用图表:

Collections in Clojure

序列

这是函数firstrest可以正确处理的 (用OO术语:“实现seq API的东西”)。

这确实可能是集合,或者是应用功能seq的结果。

如果将函数seq应用于有意义的对象,则会得到一个 seq对象,该对象表示/生成序列,可能是“按需”,即惰性, 办法。

seq对象基本上是一个支持firstrest的迭代器对象。

例如,应用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(但实际上是另一种具体类型)。

最后,创建一个空序列:(rest (seq [:foo]))

用于演示目的的奖励图像:

operations seq, first, next, rest illustrated