在 Clojure 中添加向量的惯用方法是什么?

发布于 2024-10-01 02:39:19 字数 505 浏览 7 评论 0原文

添加到列表很容易:

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

附加到向量很容易:

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

如何(惯用地)添加到向量,同时返回向量? 这不起作用,因为它返回一个 seq,而不是一个向量:

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

这很丑陋(IMVHO):

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

注意:我基本上只想要一个可以附加和前置的数据结构。附加到大型列表应该会产生很大的性能损失,所以我想到了向量。

Prepending to a list is easy:

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

Appending to a vector is easy:

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

How do I (idiomatically) prepend to a vector, while getting back a vector?
This does not work as it returns a seq, not a vector:

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

This is ugly (IMVHO):

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

Note: I basically just want a datastructure that I can append and prepend to. Appending to large lists should have a large performance penalty, so I thought of vectors..

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

我偏爱纯白色 2024-10-08 02:39:19

向量不是为前置而设计的。您只有 O(n) 前置:

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

您想要的很可能是 手指树

Vectors are not designed for prepending. You have only O(n) prepend:

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

What you want is most likely a finger tree.

萌无敌 2024-10-08 02:39:19

我知道这个问题很老了,但没有人说过任何关于差异的事情
列表,既然你说你真的只是想要一些可以附加的东西
并加上前缀,听起来差异列表可能对您有帮助。
它们在 Clojure 中似乎并不流行,但它们非常简单
实现起来比手指树复杂得多,所以我做了一个
微小的差异列表库,刚才(甚至测试了它)。这些
在 O(1) 时间内连接(前置或附加)。转换差值
列表回到列表应该花费你 O(n),这是一个很好的权衡,如果
你做了很多串联。如果你没有做很多事情
连接,然后就坚持列表,对吧? :)

以下是这个小库中的函数:

dl: 差异列表实际上是一个连接其自身的函数
包含参数的内容并返回结果列表。每次
你产生了一个差异列表,你正在创建一个小函数
就像数据结构一样。

dlempty:由于差异列表只是将其内容连接到
参数,空的差异列表与恒等式是一样的
功能。

undl:由于差异列表的作用,您可以将
只需用 nil 调用它即可将列表与普通列表区别,所以这
功能并不是真正需要的;这只是为了方便。

dlcons: 将一个项目放在列表的前面——不完全是
必要的,但 consing 是一个足够常见的操作,它只是一个
一行(就像这里的所有函数一样)。

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

现在,这是那个小库 —— 5 个单行函数,可为您提供 O(1)
附加/前置数据结构。不错吧?啊,拉姆达的美丽
微积分...

(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)))

which will return

(1 2 3 4)

I know this question is old, but no one said anything about difference
lists and since you say you really just want something you can append
and prepend with, it sounds like difference lists might help you.
They don't seem popular in Clojure, but they are VERY easy
to implement and a lot less complex than finger trees, so I made a
tiny difference list library, just now (and even tested it). These
concatenate in O(1) time (prepend or append). Converting a difference
list back to a list should cost you O(n), which is a nice trade-off if
you're doing a lot of concatenation. If you're not doing a lot of
concatenation, then just stick to lists, right? :)

Here are the functions in this tiny library:

dl: A difference list is actually a function which concats its own
contents with the argument and returns the resulting list. Every time
you produce a difference list, you're creating a little function that
acts like a data structure.

dlempty: Since a difference list just concats its contents to the
argument, an empty difference list is the same thing as the identity
function.

undl: Because of what difference lists do, you can convert a
difference list to a normal list just by calling it with nil, so this
function isn't really needed; it's just for convenience.

dlcons: conses an item to the front of the list -- not totally
necessary, but consing is a common enough operation and it's just a
one-liner (like all of the functions, here).

dlappend: concats two difference lists. I think its definition is
the most fun -- check it out! :)

And now, here's that tiny library -- 5 one-line functions that give you a O(1)
append/prepend data structure. Not bad, eh? Ah, the beauty of Lambda
Calculus...

(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))))

You can see it in action with this:

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

which returns:

(1 2 3 4 5 6)

This also returns the same thing:

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

Have fun with difference lists!

Update

Here are some definitions that may be more difficult to understand but I think are better:

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

This lets you say something like this:

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

Which would return

(1 2 3 4)
掐死时间 2024-10-08 02:39:19

如果您不担心准引用,那么这个解决方案实际上非常优雅(对于“优雅”的某些定义):

> `[~:foo ~@[:bar :baz]]

[:foo :bar :baz]

我实际上在实际代码中偶尔使用它,因为声明性语法使其非常具有可读性(恕我直言)。

If you don't fear quasiquoting, this solution is actually pretty elegant (for some definitions of 'elegant'):

> `[~:foo ~@[:bar :baz]]

[:foo :bar :baz]

I actually use this on occasion in real code, since the declarative syntax makes it pretty readable IMHO.

紫轩蝶泪 2024-10-08 02:39:19

正如用户 optevo 在手指树答案下的评论中所说,您可以使用 clojure/core。 rrb-vector lib,它实现了RRB树:

RRB-Trees 基于 Clojure 的 PersistentVectors 构建,添加了对数时间串联和切片。除了缺少 vector-of 函数之外,ClojureScript 支持相同的 API。

我决定将其作为单独的答案发布,因为我认为这个图书馆值得这样做。它支持 ClojureScript,并由 Michał Marczyk 维护,他在 Clojure 社区中因其致力于实现各种数据结构。

As the user optevo said in the comments under the finger trees answer, you can use the clojure/core.rrb-vector lib, which implements RRB-trees:

RRB-Trees build upon Clojure's PersistentVectors, adding logarithmic time concatenation and slicing. ClojureScript is supported with the same API except for the absence of the vector-of function.

I decided to post this as a separate answer, because I think this library deserves that. It supports ClojureScript and it's maintained by Michał Marczyk, who is fairly known within the Clojure community for his work on implementing various data structures.

百思不得你姐 2024-10-08 02:39:19

我建议使用便利功能 内置于图珀洛图书馆。例如:

(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

I would suggest using the convenience features built into the Tupelo Library. For example:

(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]

by comparison, with raw Clojure it is easy to make a mistake:

; 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文