【发布时间】:2011-07-18 04:16:33
【问题描述】:
[注意:标题和文本经过大量编辑,以更清楚地表明我不是特别关注字符串,而是关注一般序列,以及相同的惰性处理]
以字符序列/字符串为例,说我想把字符串转成like
"\t a\r s\td \t \r \n f \r\n"
进入
“a s d f”
更一般地说,我想将序列中的所有连续空白(或任何其他任意项集)转换为单个项,并且是懒惰的。
我想出了以下 partition-by/mapcat 组合,但想知道是否有更简单或更好的方法(可读性、性能等)来完成同样的事情。 p>
(defn is-wsp?
[c]
(if (#{\space \tab \newline \return} c) true))
(defn collapse-wsp
[coll]
(mapcat
(fn [[first-elem :as s]]
(if (is-wsp? first-elem) [\space] s))
(partition-by is-wsp? coll)))
在行动:
=> (apply str (collapse-wsp "\t a\r s\td \t \r \n f \r\n"))
" a s d f "
更新: 我以字符串/字符序列/ wsp为例,但我真正想要的是任何类型的序列上的通用函数,该函数通过某个单个预定义项折叠任意数量的连续项,这些项是预定义项集的一部分.我特别想知道是否有比 partition-by/mapcat 更好的替代方案,如果可以针对“字符串”特殊情况进行优化,则没有那么多。
更新 2:
这是一个完全惰性的版本——我担心上面的那个不是完全惰性的,除了它正在做多余的 is-wsp 吗?检查。我概括了参数名称等,因此它不仅看起来像可以通过 String.whatever() 调用轻松替换的东西 - 它是关于任意序列的。
(defn lazy-collapse
([coll is-collapsable-item? collapsed-item-representation] (lazy-collapse coll is-collapsable-item? collapsed-item-representation false))
([coll is-collapsable-item? collapsed-item-representation in-collapsable-segment?]
(let [step (fn [coll in-collapsable-segment?]
(when-let [item (first coll)]
(if (is-collapsable-item? item)
(if in-collapsable-segment?
(recur (rest coll) true)
(cons collapsed-item-representation (lazy-collapse (rest coll) is-collapsable-item? collapsed-item-representation true)))
(cons item (lazy-collapse (rest coll) is-collapsable-item? collapsed-item-representation false)))))]
(lazy-seq (step coll in-collapsable-segment?)))))
这很快,完全懒惰,但我希望能够更简洁地表达这一点,因为我自己也很懒惰。
目前为止惰性折叠器的基准: 通过查看代码很容易判断代码是否可读,但是为了了解它们在性能方面的比较,这里是我的基准测试。我首先检查该函数是否完成了它应该做的事情,然后我吐出了需要多长时间
- 创建惰性序列 1M 次
- 创建惰性序列并取第一项 1M 次
- 创建惰性序列并取第二项 1M 次
- 创建惰性序列并取最后一项(即完全实现惰性序列)1M 次
测试 1 到 3 旨在衡量至少一点点的懒惰。 我运行了几次测试,执行时间没有明显变化。
user=> (map
(fn [collapse]
(println (class collapse) (str "|" (apply str (collapse test-str is-wsp? \space)) "|"))
(time (dotimes [_ 1000000] (collapse test-str is-wsp? \space)))
(time (dotimes [_ 1000000] (first (collapse test-str is-wsp? \space))))
(time (dotimes [_ 1000000] (second (collapse test-str is-wsp? \space))))
(time (dotimes [_ 1000000] (last (collapse test-str is-wsp? \space)))))
[collapse-overthink collapse-smith collapse-normand lazy-collapse])
user$collapse_overthink | a s d f |
"Elapsed time: 153.490591 msecs"
"Elapsed time: 3064.721629 msecs"
"Elapsed time: 4337.932487 msecs"
"Elapsed time: 24797.222682 msecs"
user$collapse_smith | a s d f |
"Elapsed time: 141.474904 msecs"
"Elapsed time: 812.998848 msecs"
"Elapsed time: 2112.331739 msecs"
"Elapsed time: 10750.224816 msecs"
user$collapse_normand | a s d f |
"Elapsed time: 314.978309 msecs"
"Elapsed time: 1423.779761 msecs"
"Elapsed time: 1669.660257 msecs"
"Elapsed time: 8074.759077 msecs"
user$lazy_collapse | a s d f |
"Elapsed time: 169.906088 msecs"
"Elapsed time: 638.030401 msecs"
"Elapsed time: 1195.445016 msecs"
"Elapsed time: 6050.945856 msecs"
到目前为止的底线:最好的代码最慢,最丑陋的代码最快。我很确定它不必是这样的......
【问题讨论】:
标签: clojure