2010-11-04 67 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 
相關問題