Компактный Clojure код для совпадений регулярных выражений и их позиция в строке
Стюарт Халлоуэй приводит пример
(re-seq #"\w+" "The quick brown fox")
как естественный метод поиска совпадений совпадений регулярных выражений в Clojure. В его книге эта конструкция противопоставлена итерации над матчи. Если бы все, о чем заботились, были списком матчей, это было бы здорово. Однако, что, если мне нужны матчи и их позиция внутри строки? Есть ли лучший способ сделать это, что позволяет мне использовать существующую функциональность в java.util.regex, прибегая к чему-то вроде понимания последовательности по каждому индексу в исходной строке? Другими словами, хотелось бы набрать что-то вроде
(re-seq-map # "[0-9] +" "3a1b2c1d" )
который вернет карту с ключами в качестве позиции и значений в качестве совпадений, например.
{0 "3", 2 "1", 4 "2", 6 "1"}
Есть ли какая-либо реализация этого в уже существующей библиотеке или я должен ее написать (не должны ли строки строк)?
Ответы
Ответ 1
Вы можете извлечь данные из объекта java.util.regex.Matcher
.
user> (defn re-pos [re s]
(loop [m (re-matcher re s)
res {}]
(if (.find m)
(recur m (assoc res (.start m) (.group m)))
res)))
#'user/re-pos
user> (re-pos #"\w+" "The quick brown fox")
{16 "fox", 10 "brown", 4 "quick", 0 "The"}
user> (re-pos #"[0-9]+" "3a1b2c1d")
{6 "1", 4 "2", 2 "1", 0 "3"}
Ответ 2
Вы можете применить любую функцию к объекту java.util.regex.Matcher и вернуть его результаты (аналогично решению Брайана, но без явного loop
):
user=> (defn re-fun
[re s fun]
(let [matcher (re-matcher re s)]
(take-while some? (repeatedly #(if (.find matcher) (fun matcher) nil)))))
#'user/re-fun
user=> (defn fun1 [m] (vector (.start m) (.end m)))
#'user/fun1
user=> (re-fun #"[0-9]+" "3a1b2c1d" fun1)
([0 1] [2 3] [4 5] [6 7])
user=> (defn re-seq-map
[re s]
(into {} (re-fun re s #(vector (.start %) (.group %)))))
user=> (re-seq-map #"[0-9]+" "3a1b2c1d")
{0 "3", 2 "1", 4 "2", 6 "1"}