2010-11-04 60 views
52

预谋的名单很简单:在Clojure中添加一个向量的习惯用法是什么?

user=> (conj '(:bar :baz) :foo) 
(:foo :bar :baz) 

追加到一个向量很简单:

user=> (conj [:bar :baz] :foo) 
[:bar :baz :foo] 

我如何(地道)在前面加上一个载体,而取回一个载体? 这不工作,因为它返回一个序列,而不是一个向量:

user=> (cons :foo [:bar :baz])  
(:foo :bar :baz) 

这是丑陋的(IMVHO):

user=> (apply vector (cons :foo [:bar :baz])) 
[:foo :bar :baz] 

注:我基本上只是希望有一个数据结构,我可以追加和前插至。追加到大列表应该有一个很大的性能损失,所以我想到了矢量..

+0

我是不能不指出的是,最后的“丑陋”的例子可以简化成稍微不那么丑陋的形式: ''(申请矢量:foo [:bar:baz])'(只是拿出'cons')。 但我同意它有点尴尬,超出'(vector ...)'解决方案,基本上只有'concat'。 如果只有splatting参数的sugary/pretty语法,而不是'apply'(就像'〜@',但不仅仅是宏)...... *叹息* – chbrown 2017-04-11 06:02:19

回答

67

矢量不是预先设计的。你只有O(n)的前置:

user=> (into [:foo] [:bar :baz]) 
[:foo :bar :baz] 

你想要什么是最有可能是finger tree

+0

手指树+1 - 非常酷的数据结构! – mikera 2010-11-04 14:00:22

+1

也可以在线放置幻灯片的好方法:http://talk-finger-tree.heroku.com – 0x89 2010-11-04 18:15:17

+2

rrb-vectors可以在O(log n)时间中连接(并因此预连接)。见https://github.com/clojure/core.rrb-vector – optevo 2015-09-13 03:27:34

15

我知道这个问题是旧的,但没有人说,大约差什么 列表和既然你说你真的只是想要的东西,你可以追加 ,并,这听起来像差异表可以帮助你在前面加上。 它们在Clojure中似乎并不流行,但它们非常容易实现,并且比手指树简单得多,所以我刚刚制作了一个 微小差异列表库(甚至测试了它)。这些 连接在O(1)时间(前置或追加)。将差异 列表转换回列表应该花费你O(n),这是一个很好的折衷,如果 你做了很多连接。如果你没有做很多 级联,那么只需坚持列表,对吧? :)

下面是在这个小小的库中的函数:

DL:的差异列表实际上是与参数concats自己的 内容,然后返回结果列表的功能。每次你产生一个差异列表 ,你就创建了一个小功能,它的作用就像一个数据结构。

dlempty:由于差异列表只是concats其内容的 说法,一个空的差异列表是一回事身份 功能。

undl:因为什么区别名单,则可以只用零调用它转换 差异列表到正常的列表,所以是不是真的需要这个 功能;这只是为了方便。

dlcons: conses之外到列表中的前一个项目 - 并非完全 必要的,但consing是一种常见的足够操作,它只是一个 的一行(像所有的功能,在这里)。

dlappend:连接两个差异列表。我认为它的定义是 最有趣 - 检查出来! :)

现在,这里是一个小型库 - 5个单行函数,给你一个O(1) append/prepend数据结构。不错,呃?嗯,LAMBDA 微积分的美丽......

(defn dl 
    "Return a difference list for a list" 
    [l] 
    (fn [x] (concat l x))) 

; Return an empty difference list 
(def dlempty identity) 

(defn undl 
    "Return a list for a difference list (just call the difference list with nil)" 
    [aDl] 
    (aDl nil)) 

(defn dlcons 
    "Cons an item onto a difference list" 
    [item aDl] 
    (fn [x] (cons item (aDl x)))) 

(defn dlappend 
    "Append two difference lists" 
    [dl1 dl2] 
    (fn [x] (dl1 (dl2 x)))) 

你可以用这个看到它在行动:

(undl (dlappend (dl '(1 2 3)) (dl '(4 5 6)))) 

返回:

(1 2 3 4 5 6) 

这也返回同样的事情:

((dl '(1 2 3)) '(4 5 6)) 

与差异列表玩得开心!

更新

这里有一些定义可能更难以理解,但我认为是更好的:

(defn dl [& elements] (fn [x] (concat elements x))) 
(defn dl-un [l] (l nil)) 
(defn dl-concat [& lists] (fn [x] ((apply comp lists) x))) 

这让你说这样的事情:

(dl-un (dl-concat (dl 1) (dl 2 3) (dl) (dl 4))) 

这将返回

(1 2 3 4) 
2

当用户optevo在手指树回答下面的评论说,你可以使用clojure/core.rrb-vector lib中,它实现RRB树:

RRB树建立在Clojure的PersistentVectors,增加对数时间串联和分片。除了缺少vector-of函数以外,ClojureScript还支持相同的API。

我决定张贴这个作为一个单独的答案,因为我认为这个图书馆应该。它支持ClojureScript,并且由Michał Marczyk维护,他在Clojure社区中相当知名,因为他在实现各种数据结构方面所做的工作。

2

我会建议使用便利功能built into the Tupelo Library。例如:

(append [1 2] 3 ) ;=> [1 2 3 ] 
(append [1 2] 3 4) ;=> [1 2 3 4] 

(prepend 3 [2 1]) ;=> [ 3 2 1] 
(prepend 4 3 [2 1]) ;=> [4 3 2 1] 

通过比较,与原始的Clojure很容易犯错误:

; Add to the end 
(concat [1 2] 3) ;=> IllegalArgumentException 
(cons [1 2] 3) ;=> IllegalArgumentException 
(conj [1 2] 3) ;=> [1 2 3] 
(conj [1 2] 3 4) ;=> [1 2 3 4] 

; Add to the beginning 
(conj  1 [2 3]) ;=> ClassCastException 
(concat 1 [2 3]) ;=> IllegalArgumentException 
(cons  1 [2 3]) ;=> (1 2 3) 
(cons 1 2 [3 4]) ;=> ArityException 
相关问题