在 Clojure 中,什么时候应该在列表上使用向量,反之亦然?

In Clojure, when should I use a vector over a list, and the other way around?

提问人:Rayne 提问时间:7/19/2009 最后编辑:ROMANIA_engineerRayne 更新时间:10/31/2017 访问量:31243

问:

我读到向量不是序列,但列表是。我不确定使用一个而不是另一个的理由是什么。似乎向量使用最多,但这是有原因的吗?

列出 向量 clojure 序列

评论

1赞 Duncan McGregor 4/14/2013
相关 stackoverflow.com/questions/6928327/...

答:

98赞 C. K. Young 7/19/2009 #1

如果您做过很多 Java 编程,并且熟悉 Java 集合框架,请考虑像 这样的列表和像 这样的向量。因此,您几乎可以以相同的方式选择容器。LinkedListArrayList

进一步澄清:如果您打算将项目单独添加到序列的前面或后面很多,链表比向量要好得多,因为不需要每次都对项目进行洗牌。但是,如果您想经常获取特定元素(而不是在列表的前面或后面)(即随机访问),您将需要使用向量。

顺便说一句,向量可以很容易地转换为序列。

user=> (def v (vector 1 2 3))
#'user/v
user=> v
[1 2 3]
user=> (seq v)
(1 2 3)
user=> (rseq v)
(3 2 1)

评论

0赞 Rayne 7/19/2009
向量不是序列,但它们是顺序的。(来源:Rich 本人在 freenode 上的 #clojure。另外,我根本不了解 Java,但 Rich 确实回答了我的问题。
1赞 C. K. Young 7/19/2009
我将编辑我的帖子,说,可以通过 seq 函数将向量制作成 seqs。:-)
2赞 Rayne 7/19/2009
选择你的答案是因为它确实回答了问题,我真的不喜欢选择我自己的答案是正确的。似乎不对。谢谢。:)
0赞 boxed 12/24/2013
在添加第一个和最后一个的情况下,deque 比链表更好。LL是相当可怕的:P
1赞 C. K. Young 1/20/2015
@boxed 你不能在向量之上实现一个 deque,或者没有有效地重新实现你自己。ArrayListArrayDeque
53赞 Svante 7/19/2009 #2

向量具有 O(1) 个随机访问时间,但必须预先分配。列表可以动态扩展,但访问随机元素是 O(n)。

评论

3赞 C. K. Young 7/19/2009
从技术上讲,链表有 O(1) 次访问时间...如果您仅访问 front 或 back 元素。9-3但是,向量确实具有 O(1) 随机访问。:-)
4赞 C. K. Young 7/19/2009
(如上所述,“链表”是指双向链表。单向链表仅对前元素具有 O(1) 访问权限。:-P)
1赞 keithjgrant 6/20/2014
作为一个刚刚潜入 Clojure 的人,这是一个比其他两个票数更多的答案更好的答案。另外两个告诉我没什么用。
0赞 Gill Bates 5/11/2015
@ChrisJester-Young 单链表可以支持对 back 的 O(1) 访问,如果它存储了对 back 元素的引用,就像这样
0赞 Allan David 11/3/2022
我认为 Clojure 中的向量不是作为链表实现的。查看此处的文档,他们说访问权限为 O(log32N)。clojure.org/reference/......
125赞 Rayne 7/19/2009 #3

再一次,我似乎已经回答了自己的问题,变得不耐烦并在 Freenode 上 #clojure 地问了它。好在 Stackoverflow.com :D上回答你自己的问题

我和 Rich Hickey 进行了简短的讨论,以下是它的要点。

[12:21] <Raynes>    Vectors aren't seqs, right?
[12:21] <rhickey>   Raynes: no, but they are sequential
[12:21] <rhickey>   ,(sequential? [1 2 3])
[12:21] <clojurebot>    true
[12:22] <Raynes>    When would you want to use a list over a vector?
[12:22] <rhickey>   when generating code, when generating back-to-front
[12:23] <rhickey>   not too often in Clojure

评论

1赞 C. K. Young 7/19/2009
当你在freenode上时,来到黑暗面并加入 #stackoverflow!9-3
0赞 Rayne 7/19/2009
我实际上曾经在那里闲着。我切换了 IRC 客户端,从未想过将 #stackoverflow 添加到我的自动加入列表中。
0赞 Rob Grant 2/25/2014
我是Lisp的新手,但我想知道向量、映射和集合是否在某种程度上打破了所有代码都可以与数据互换的想法?或者这只是使 Clojure 成为实用 Lisp 的原因之一?(或者,你能计算一个向量吗?
33赞 Jimmy Hoffa 2/26/2014
这是完全无用的聊天片段。“生成代码”“从后到前生成”——>确切地说意味着什么??我真的很难回答这个问题,因为在我的书中,懒惰 + 声明式风格 = 更好的性能,但 Clojure 中到处都建议向量,这让我完全困惑。
26赞 omiel 4/4/2014
@JimmyHoffa 我的理解方式:“生成代码”=“宏内部”(因为大部分代码都是函数调用,因此是列表);“从后到前生成” = “通过预置构建序列”。
15赞 Arthur Ulfeldt 7/21/2009 #4

只是一个简短的旁注:

"I read that Vectors are not seqs, but Lists are." 

序列比列表或向量(或映射或集合)更通用。
不幸的是,REPL 打印的列表和序列是相同的,因为它确实使列表看起来像是序列,即使它们不同。(seq)函数将从许多不同的东西(包括列表)中创建一个序列,然后你可以将该序列提供给任何一个使用seq做漂亮事情的函数。

user> (class (list 1 2 3))
clojure.lang.PersistentList

user> (class (seq (list 1 2 3)))
clojure.lang.PersistentList

user> (class (seq [1 2 3]))
clojure.lang.PersistentVector$ChunkedSeq

Sec 有一个快捷方式,如果它已经是 seq,则返回其参数:

user> (let [alist (list 1 2 3)] (identical? alist (seq alist)))
true
user> (identical? (list 1 2 3) (seq (list 1 2 3)))
false

static public ISeq seq(Object coll){
        if(coll instanceof ASeq)
                return (ASeq) coll;
        else if(coll instanceof LazySeq)
                return ((LazySeq) coll).seq();
        else
                return seqFrom(coll);
}

列表是序列,尽管其他东西也是,但并非所有序列都是列表。

评论

0赞 Arthur Ulfeldt 7/21/2009
我并不是要挑一个小问题,它只是一个指出有用的东西的机会。许多人已经知道这个:)
2赞 qerub 8/4/2012
你不是说而不是?classclass?
0赞 Adrian Mouat 10/25/2013
不确定您的示例在 clojure 更新后是否发生了变化(我想我使用的是 1.5),但您的两个示例都为我返回。我假设你的意思是不写.clojure.lang.PersistentListclassclass?
0赞 Arthur Ulfeldt 10/26/2013
我确实做到了!我会解决这个问题的
0赞 johnbakers 1/3/2014
还是有点困惑;因为为您提到的这两个表达式返回相同的 PersistentList,这意味着序列和列表确实是完全相同的东西?class
31赞 mikera 11/24/2011 #5

何时使用向量:

  • 索引访问性能 - 索引访问的成本为 ~O(1),而列表的成本为 O(n)
  • 附加 - with conj 是 ~O(1)
  • 方便的符号 - 我发现在任何一个都可以工作的情况下,键入和阅读 [1 2 3] 比 '(1 2 3) 更容易。

何时使用列表:

  • 当您想将其作为序列访问时(因为列表直接支持 seq 而不必分配新对象)
  • 前缀 - 添加到带有缺点或最好是 conj 的列表的开头是 O(1)

评论

3赞 boxed 12/24/2013
即使在两端添加/删除列表也是一个非常糟糕的选择。deque 要好得多(在 CPU 中,尤其是内存中)。尝试 github.com/pjstadig/deque-clojure
2赞 Merlyn Morgan-Graham 3/29/2016
回复:对于那些对此成本解释可能有帮助的人 - stackoverflow.com/questions/200384/constant-amortized-time~O(1)
0赞 Allan David 11/3/2022
该访问在 clojure 文档中被描述为 O(log32N) clojure.org/reference/...
0赞 mikera 12/15/2022
请注意,从技术上讲,如果 N 可以增长到无穷大,其中一些将是 O(log32 N),但实际上这等价于 Clojure 中的 O(1),因为 N 被限制到一个固定的最大大小 (int)