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

这是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")]

编辑了
恭喜你找到了解决该问题的方案。

不幸的是,似乎这种粘性会导致re-seq与其他表达式相比与CLJ的不一致

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

;; CLJS (sticky)
(re-seq (js/RegExp. #"[a-f]" "y") "aabcded")
;; => ("a" "b" "e")

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

编辑了

据我所知,我最终通过以下方式重新实现了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(并将正则表达式“global”设置为开启),它就按照我的测试用例的正常方式工作。

0

我为Jira工单创建了一个补丁。
解决方案是在正则表达式中添加一个全局标志(如果尚未添加),并反复调用RegExp.prototype.exec()方法,直到不再有匹配项。
如果您发现任何问题,请告知我。

...