clojure:pop和push

时间:2016-02-13 21:28:06

标签: clojure functional-programming clojurescript

我正在寻找一种适合以下操作的顺序数据结构。列表的长度保持不变,它永远不会长于或短于固定长度。

省略第一项并将x添加到最后。

(0 1 2 3 4 5 6 7 8 9)

(pop-and-push "10")

(1 2 3 4 5 6 7 8 9 10)

只有一个其他阅读操作必须经常进行:

(last coll)

pop-and-push可以像这样实现:

(defn pop-and-push [coll x]
   (concat (pop coll) ["x"]))

(遗憾的是,这不适用于例如范围产生的序列,它只会在文字'(..)声明的序列传递时弹出。)

但这是最佳的吗?

1 个答案:

答案 0 :(得分:4)

此处的主要问题(我们将"x"更改为x后)是concat返回lazy-seq,而lazy-seqs是pop无效的参数}。

user=> (defn pop-and-push [coll x] (concat (pop coll) [x]))
#'user/pop-and-push
user=> (pop-and-push [1 2 3] 4)
(1 2 4)
user=> (pop-and-push *1 5)
ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.IPersistentStack  clojure.lang.RT.pop (RT.java:730)

我天真的偏好是使用矢量。使用subvec可以轻松实现此功能。

user=> (defn pop-and-push [v x] (conj (subvec (vec v) 1) x))
#'user/pop-and-push
user=> (pop-and-push [1 2 3] 4)
[2 3 4]
user=> (pop-and-push *1 5)
[3 4 5]

如您所见,此版本实际上可以根据自己的返回值进行操作

正如评论中所建议的,PersistentQueue是针对这种情况制作的:

user=> (defn pop-and-push [v x] (conj (pop v) x))
#'user/pop-and-push
user=> (pop-and-push (into clojure.lang.PersistentQueue/EMPTY [1 2 3]) 4)
#object[clojure.lang.PersistentQueue 0x50313382 "clojure.lang.PersistentQueue@7c42"]
user=> (into [] *1)
[2 3 4]
user=> (pop-and-push *2 5)
#object[clojure.lang.PersistentQueue 0x4bd31064 "clojure.lang.PersistentQueue@8023"]
user=> (into [] *1)
[3 4 5]

PersistentQueue数据结构虽然在某些方面使用不太方便,但实际上已针对此用途进行了优化。