Clojure 中惯用的字符串旋转
Posted
技术标签:
【中文标题】Clojure 中惯用的字符串旋转【英文标题】:Idiomatic string rotation in Clojure 【发布时间】:2015-02-25 14:29:20 【问题描述】:如何在 Clojure 中为 Burrows-Wheeler transform 惯用地旋转字符串?
我想出了这个,它使用(cycle "string")
,但感觉有点必要:
(let [s (str "^" "banana" "|")
l (count s)
c (cycle s)
m (map #(take l (drop % c)) (range l))]
(apply map str m))
=> ("^banana|" "banana|^" "anana|^b" "nana|^ba" "ana|^ban" "na|^bana" "a|^banan" "|^banana")
我不确定这是否符合代码高尔夫的条件。有没有更清洁的方法来做到这一点?
【问题讨论】:
在效率的基础上,Criterium 的 quickbench 以 1:7:46 的粗略比例给出了 cgrand/status203/pate 答案的平均运行时间 @status20,@O-I 的rotations
解决方案甚至快了 4 倍。
@pate rotations 返回一个惰性字符序列的惰性序列。当我强制评估时((dorun (apply str (rotations "^bananas|")))
vs (dorun (bwrot "bananas"))
)rotations
比 bwrot
慢 12 倍。
【参考方案1】:
我愿意:
(defn bwrot [s]
(let [s (str "^" s "|")]
(for [i (range (count s))]
(str (subs s i) (subs s 0 i)))))
或:
(defn bwrot [s]
(let [n (+ 2 (count s))
s (str "^" s "|^" s "|")]
(for [i (range n)]
(subs s i (+ i n)))))
第二个应该分配更少(一个字符串而不是每次迭代三个)。
【讨论】:
从 Java 7 开始,子字符串不再是视图。 谢谢@Alex 我不知道那个变化(这显然发生在 7u6)【参考方案2】:clojure.contrib.seq
中曾经有一个 rotations
函数,可能值得一看以获得灵感。 source 转载如下:
(defn rotations
"Returns a lazy seq of all rotations of a seq"
[x]
(if (seq x)
(map
(fn [n _]
(lazy-cat (drop n x) (take n x)))
(iterate inc 0) x)
(list nil)))
然后你可以这样做:
(apply map str (rotations "^banana|"))
; => ("^banana|" "banana|^" "anana|^b" "nana|^ba" "ana|^ban" "na|^bana" "a|^banan" "|^banana")
【讨论】:
【参考方案3】:逐步调用partition
有效:
(defn bwt[s]
(let [s' (str "^" s "|")
c (cycle s')
l (count s')]
(map last (sort (apply map str (take l (partition l 1 c)))))))
(apply str (bwt "banana"))
=> "|bnn^aaa"
【讨论】:
这比 @O-I 的rotations
解决方案慢 72 倍。【参考方案4】:
如果我不关心效率或字符数,我会写如下内容:
(defn rotate-string
[s]
(apply str (concat (drop 1 s) (take 1 s))))
(defn string-rotations
[s]
(->> s
(iterate rotate-string)
(take (count s))))
(rotate-string "^banana|") ; "banana|^"
(string-rotations "^banana|") ; ("^banana|" "banana|^" "anana|^b" "nana|^ba" "ana|^ban" "na|^bana" "a|^banan" "|^banana")
特别是,将单次旋转分解为它自己的函数。
【讨论】:
【参考方案5】:另一种实现旋转的方法是使用“双字符串”(即将字符串连接到自身)并使用子字符串。
(defn rotations [strng]
(let [indices (range (count strng))
doublestr (str strng strng)]
(map #(subs doublestr % (+ % (count strng))) indices)))
(rotations "^banana|")
;;(^banana| banana|^ anana|^b nana|^ba ana|^ban na|^bana a|^banan |^banana)
“foo”的旋转:
取双字符串“foofoo” “foo”的长度n
= 3
旋转是 "foofoo" 的所有 n
子字符串,以 indices
0、1、2 开头并且具有相同的长度 n
【讨论】:
以上是关于Clojure 中惯用的字符串旋转的主要内容,如果未能解决你的问题,请参考以下文章