【问题标题】:clojure: pop and pushclojure:弹出和推送
【发布时间】:2016-02-13 21:28:05
【问题描述】:

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

省略第一项并在末尾添加 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"]))

(不幸的是,这不适用于例如 range 生成的序列,它只会在传递由文字 '(..) 声明的序列时弹出。)

但这是最优的吗?

【问题讨论】:

  • 描述'()(range n) 问题的更清晰的方法是pop 的这种用法适用于列表,但不适用于其他集合类型(对于惰性有各种不正确的行为- seqs、vectors、sets、arrays、strings 等,从显式错误到只得到错误输出而没有检测到错误)。

标签: clojure functional-programming clojurescript


【解决方案1】:

这里的主要问题(一旦我们将"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]

如你所见,这个版本其实可以对自己的返回值进行操作

正如 cmets 中所建议的,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 数据结构虽然在某些方面使用起来不太方便,但实际上已针对这种用法进行了优化。

【讨论】:

    猜你喜欢
    • 2012-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    相关资源
    最近更新 更多