Я пытаюсь написать функцию с именем scan-for
, которая, принимая в качестве входных данных набор строк («токены»), возвращает функцию «токенизатор», которая, принимая в качестве входных данных строку, возвращает (предпочтительно ленивый)последовательность строк, состоящая из «токенов», содержащихся во входных данных, распознаваемых жадным способом, и непустых подстрок между ними, в начале и конце, в порядке их появления на входе.
Например, ((scan-for ["an" "ban" "banal" "d"]) "ban bananas and banalities")
должно выдать:
("ban" " " "ban" "an" "as " "an" "d" " " "banal" "ities")
В моей первой попытке я использую регулярное выражение, чтобы сопоставить "токены" (с re-seq
) и найти промежуточные подстроки(с split
) и затем чередуйте получающиеся последовательности.Проблема состоит в том, что входная строка анализируется дважды с помощью построенного регулярного выражения и что результирующая последовательность не является ленивой из-за split
.
[В определении scan-for
я использую молчаливый / point-free style (исключая лямбды и их замаскированные маскировки), которые я нахожу элегантными и полезными в целом ( Джон Бэкус, вероятно, согласится ).В ближайшем будущем это требует расширенного использования partial
, чтобы заботиться о неиспользованных функциях.Если вам это не нравится, вы можете добавить лямбда-выражения, макросы потоков и т. Д.]
(defn rpartial
"a 'right' version of clojure.core/partial"
[f & args] #(apply f (concat %& args)))
(defn interleave*
"a 'continuing' version of clojure.core/interleave"
[& seqs]
(lazy-seq
(when-let [seqs (seq (remove empty? seqs))]
(concat
(map first seqs)
(apply interleave* (map rest seqs))))))
(defn make-fn
"makes a function from a symbol and an (optional) arity"
([sym arity]
(let [args (repeatedly arity gensym)]
(eval (list `fn (vec args) (cons sym args)))))
([sym] (make-fn sym 1)))
(def scan-for
(comp
(partial comp
(partial remove empty?)
(partial apply interleave*))
(partial apply juxt)
(juxt
(partial rpartial clojure.string/split)
(partial partial re-seq))
re-pattern
(partial clojure.string/join \|)
(partial map (make-fn 'java.util.regex.Pattern/quote))
(partial sort (comp not neg? compare))))
Во второй попытке я использую регулярное выражение для сопоставления "токенов" и промежуточных одиночных символов, а затемсгруппировать эти отдельные символы.Здесь мне не нравится объем обработки, выполняемый вне соответствия регулярному выражению.
(defn scan-for [tokens]
(comp
(partial remove empty?)
(fn group [s]
(lazy-seq
(if-let [[sf & sr] s]
(if (or (get sf 1)
(some (partial = sf) tokens))
(list* "" sf (group sr))
(let [[gf & gr] (group sr)]
(cons (str sf gf) gr)))
(cons "" nil))))
(->> tokens
(sort (comp not neg? compare))
(map #(java.util.regex.Pattern/quote %))
(clojure.string/join \|)
(#(str % "|(?s)."))
(re-pattern)
(partial re-seq))))
Так есть ли способ сделать это, используя некоторое подходящее регулярное выражение для однократного анализа ввода и выполняя минимальную обработку вне этого анализа?
(Ленивая версия split
, которая также возвращает совпадения с регулярным выражением, была бы полезна ... если бы она существовала.)