[Примечание: заголовок и текст были сильно отредактированы, чтобы было яснее, что я не особенно после строк, но после общих последовательностей и их ленивой обработки]
Используя последовательность символов / строки в качестве примера, скажем, я хочу превратить строку как
"\ t a \ r s \ td \ t \ r \ n f \ r \ n"
в
"a s d f"
В более общих чертах я хочу превратить все непрерывные пробелы (или любой другой произвольный набор элементов) в последовательности в один элемент, и это лениво.
Я придумал следующую комбинацию partition-by / mapcat , но интересуюсь, есть ли более простые или иные лучшие способы (удобочитаемость, производительность, что угодно) для достижения того же самого.
(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, но на самом деле мне нужна универсальная функция для последовательностей любого типа, которая объединяет произвольное количество смежных элементов, которые являются частью предварительно определенного набора элементов, одним отдельным предопределенным элементом. , Мне особенно интересно узнать, есть ли лучшие альтернативы для секционирования по / mapcat, не так много, если это можно оптимизировать для особого случая 'string'.
Обновление 2 :
Вот полностью ленивая версия - я боюсь, что вышеприведенная не совсем ленивая, кроме того, что она делает избыточный is-wsp? чеки. Я обобщил имена параметров и т. Д., Чтобы это не выглядело просто как то, что вы можете легко заменить вызовом String.whwhat (), - это о произвольных последовательностях.
(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?)))))
Это быстро, полностью лениво, но я бы хотел выразить это более кратко, так как сам я очень ленив.
Тесты ленивых сборщиков пока что :
Легко ли судить о том, читается ли код или нет, посмотрев на код, но для того, чтобы увидеть, как они сравниваются с точки зрения производительности, вот мои критерии. Сначала я проверяю, выполняет ли функция то, что она должна делать, а затем выкладываю, сколько времени потребуется до
- создать ленивый seq 1M раз
- создайте ленивый seq и возьмите первый предмет 1M раз
- создайте ленивый seq и возьмите второй предмет 1M раз
- создайте ленивый seq и возьмите последний элемент (то есть полностью реализуйте ленивый seq) 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"
Итог: лучший код самый медленный, самый уродливый код самый быстрый. Я почти уверен, что так не должно быть ...