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")

这是否是一个bug?

ClojureScript 版本 1.10.520

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

4 答案

0
 
最佳答案

由 Alex Miller 登记为这里的 bug: https://clojure.atlassian.net/browse/CLJS-3187

+3
by

这是 ClojureScript 中 re-seq 实现中的一个错误。

(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
对找到该问题解决方案表示赞赏。

遗憾的是,粘性似乎破坏了与 CLJ 的兼容性,导致其他表达式无法使用 re-seq。

;; 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()方法,直到没有更多匹配项。
如果您发现任何问题,请告诉我。

...