请在2024 Clojure状态调查中分享您的想法!

欢迎!请在关于页面查看有关此信息。

+2
ClojureScript
编辑

你好,

我在ClojureScript的re-seq与Clojure中看到了这个差异

;; CLJ
(re-seq #"^[a-f]" "aabcded") ;; => ("a")

;; CLJS
(re-seq #"^[a-f]" "aabcded") ;; => ("a" "a" "b" "c" "d" "e" "d")

这是错误吗?

ClojureScript版本1.10.520

(记录在https://clojure.atlassian.net/browse/CLJS-3187)

4个答案

0
 
最佳答案

由Alex Miller记录为https://clojure.atlassian.net/browse/CLJS-3187中的错误

+3
by

这是ClojureScript中re-seq实现中的一个bug

(defn re-seq
  "Returns a lazy sequence of successive matches of re in s."
  [re s]
  (let [match-data (re-find re s)
        match-idx (.search s re)
        match-str (if (coll? match-data) (first match-data) match-data)
        post-idx (+ match-idx (max 1 (count match-str)))
        post-match (subs s post-idx)]
    (when match-data (lazy-seq (cons match-data (when (<= post-idx (count s)) (re-seq re post-match)))))))

问题出现在它递归地用字符串的剩余部分调用re-seq,这样做意味着^[a-f]将再次与这个新、较短的字符串匹配。

一个解决方案是将你的正则表达式设置为粘性边界匹配

(js/RegExp. #"^." "y")

这会让你的正则表达式的后续使用明白之前匹配的哪个地方,请注意,你需要准确地放置此代码,因为它需要在正确的位置创建,它不可以是全局的!如果它是全局的,你将会遇到一些奇怪的状态问题,就像这个一个。

(let [re (js/RegExp. #"^." "y")]
  [(re-seq re "cccc")
   (re-seq re "abbb")])
;; => [("c" "c") nil]

(我完全无法解释!)

可能有一个re-seq的替换实现会为你克隆出这个初始的实例

(defn re-seq2
  "Returns a lazy sequence of successive matches of re in s."
  [re s]
  (let [re-seq* (fn re-seq* [re s]
                  (let [match-data (re-find re s)
                        match-idx (.search s re)
                        match-str (if (coll? match-data) (first match-data) match-data)
                        post-idx (+ match-idx (max 1 (count match-str)))
                        post-match (subs s post-idx)]
                    (when match-data (lazy-seq (cons match-data (when (<= post-idx (count s)) (re-seq* re post-match)))))))]
    (re-seq* (js/RegExp. re "y") s)))

(let [re #"^."]
  [(re-seq2 re "cccc")
   (re-seq2 re "abbb")])
;; => [("c") ("a")]
by
编辑 by
恭喜找到解决问题的方案。

不幸的是,粘性边界匹配似乎破坏了re-seq以与CLJ中的其他表达式保持一致

;; CLJ
(re-seq #"[a-f]" "aabcded")
;; => ("a" "a" "b" "c" "d" "e" "d")

;; CLJS (粘性边界匹配)
(re-seq (js/RegExp. #"[a-f]" "y") "aabcded")
;; => ("a" "b" "e")

;; CLJS (re-seq2)
(re-seq2 #"[a-f]" "aabcded")
;; => ("a" "b" "d" "d")
0
by
编辑 by

顺便说一句,我通过以下方式重新实现了re-seq来解决当前的问题。

(defn re-seq [re s]
  (let [re* (js/RegExp. re "g")
        xf (comp (take-while some?)
                 (map first))]
    (sequence xf (repeatedly #(.exec re* s)))))

一旦从等式中移除了lazy-seq(并且将正则表达式中的“全局”选项启用),它在我的测试用例中按预期工作。

0

我为Jira票据创建了一个补丁。
解决方案是在正则表达式中添加一个全局标志,如果之前没有添加的话,然后不断地调用RegExp.prototype.exec()方法,直到没有更多匹配项。
如果您发现任何问题,请告诉我。

...