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

欢迎!请参阅关于页面以了解更多关于如何使用本站的信息。

+2
in ClojureScript by
edited by

你好,

我观察到 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
by
 
最佳答案

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

...