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(并且将“全局”切换为正则表达式),它在我的测试用例中就能按预期工作。

0

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

...