在Clojure中,何时应在列表上使用向量,反之亦然?


Answers:


112

再次,我变得不耐烦并在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

当您在freenode上时,来到黑暗的一面,加入#stackoverflow!:-P
克里斯·杰斯特·杨

我实际上曾经在那里闲着。我切换了IRC客户端,从没想过将#stackoverflow添加到我的自动加入列表中。
雷恩

我是Lisp的新手,但我想知道向量,地图和集合是否以某种方式打破了所有代码都可以与数据互换的想法吗?还是这仅仅是使Clojure成为实用的Lisp的原因之一?(或者,您可以评估向量吗?)
Rob Grant

23
这是完全无用的聊天片段。“生成代码”“从后到前生成”->就是指?我确实很难解决这个问题,因为在我的书中,懒惰+声明式风格=更好的性能,但是Clojure中到处都建议使用向量,这使我完全困惑。
Jimmy Hoffa 2014年

22
@JimmyHoffa我的理解方式是:“正在生成代码” =“在宏内”(因为大多数代码是函数调用,因此列出了这些代码);“ generate back to front” =“通过前置构建序列”。
omiel 2014年

87

如果您已经做了大量Java编程,并且熟悉Java集合框架,那么可以考虑使用list LinkedList以及vectors这样的向量ArrayList。因此,您几乎可以以相同的方式选择容器。

为了进一步说明:如果您打算将项目分别添加到序列的前面或后面很多,则链表比矢量要好得多,因为不需要每次都在项目周围进行随机排序。但是,如果您想频繁获取特定元素(不在列表的前面或后面)(即随机访问),则需要使用vector。

顺便说一下,向量可以很容易地变成序列。

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)

向量不是序列,但它们是顺序的。(来源:Rich在freenode上的#clojure上。)而且,我一点也不了解Java,但是Rich只是回答了我的问题。
雷恩

1
我将编辑我的帖子说,可以通过seq函数将向量制成 seqs。:-)
克里斯·杰斯特·杨

2
选择您的答案,因为它确实回答了问题,我真的不喜欢选择自己的答案正确。似乎不正确。谢谢。:)
Rayne

在添加第一个和最后一个的情况下,双端队列比链接列表更好。LL非常糟糕:P
装箱

1
@boxed您不能在vector之上实现双端队列,或者ArrayList没有有效地重新实现ArrayDeque自己。
克里斯·杰斯特·杨

43

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


3
从技术上讲,链表的访问时间为O(1)...如果仅访问前元素或后元素。:-P但是,向量确实具有O(1)随机访问。:-)
克里斯·杰斯特·杨

4
(如上所述,“链表”是指双链表。单链表仅具有对前元素的O(1)访问。:
克里斯·杰斯特·杨

1
当有人刚接触Clojure时,这是比其他两个获得更多票的方式更好的答案。另外两个告诉我没有用。
keithjgrant 2014年

@ ChrisJester-杨单链接列表可以支持O(1)访问回来,如果它存储到后面的元素的引用,
Gill Bates 2015年

30

何时使用向量:

  • 索引访问性能-索引访问的费用为O(1),列表的费用为O(n)
  • 附加-使用conj为〜O(1)
  • 方便的表示法-我发现在任何一种都可以使用的情况下,文字列表的键入和阅读[1 2 3]比'(1 2 3)都容易。

何时使用清单:

  • 当您要按顺序访问它时(由于列表直接支持seq而不需要分配新对象)
  • 前置-使用cons或优选conj添加到列表的开头是O(1)

3
即使在两端添加/删除列表,也是一个非常糟糕的选择。双端队列更好(在CPU尤其是内存中)。尝试github.com/pjstadig/deque-clojure
装箱

2
回复:~O(1)对于那些对此费用解释可能有所帮助的人来说-stackoverflow.com/questions/200384/constant-amortized-time
Merlyn Morgan-Graham

13

简要说明一下:

“我读到Vector不是seq,而List是。” 

序列比列表或向量(或图或集合)更通用。
不幸的是,REPL会以相同的方式打印列表和序列,因为即使它们不同,它实际上也会使列表看起来像是序列。(seq)函数将从包括列表在内的许多不同事物中构成一个序列,然后您可以将该seq馈送到对seqs做一些漂亮事情的众多函数中的任何一个。

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);
}

列表是序列,尽管其他事物也是如此,并非所有序列都是列表。


我的意思不是说一个小问题,这只是指出一个有用的东西的机会。许多人已经知道这一点了:)
亚瑟·乌尔费尔特

2
你不是要class代替class?吗?
qerub 2012年

不知道Clojure更新后您的示例是否已更改(我认为我的版本是1.5),但是您的两个示例都返回clojure.lang.PersistentList了我。我假设你的意思是写classclass?
Adrian Mouat 2013年

我确实做到了!我会解决这个问题
Arthur Ulfeldt

还是有点困惑;由于class针对您提到的这两个表达式返回相同的PersistentList,这意味着序列和列表确实是完全一样的东西吗?
johnbakers 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.