2024 State of Clojure 调查! 中分享您的想法。

欢迎!请参阅 关于页面 以获取更多关于如何使用的信息。

0 投票
ClojureScript

re-matches 函数没有正确的语义:它在字符串和匹配字符串不相等时返回 nil(执行搜索而不是匹配)。这不同于真正的匹配,就像在模式的开始和结束插入 "^" 和 "$" 一样。

Clojure 示例

user=> (re-find #"0|[1-9][0-9]+|0[xX][0-9a-zA-Z]+" "0x1") "0" user=> (re-matches #"0|[1-9][0-9]+|0[xX][0-9a-zA-Z]+" "0x1") "0x1"

比较 ClojureScript

ClojureScript:cljs.user> (re-find #"0|[1-9][0-9]+|0[xX][0-9a-zA-Z]+" "0x1") "0" ClojureScript:cljs.user> (re-matches #"0|[1-9][0-9]+|0[xX][0-9a-zA-Z]+" "0x1") nil

这是 CLJS-775(之一)的原因。

我不完全确定这里要做什么。我的第一个想法是让 re-matches 检查其正则表达式输入的 -source 属性,然后用 "^$" 包装字符串,然后将所有标志仔细复制到一个新的正则表达式。

问题
1. 有没有哪些模式在这种情况下是不安全的?例如,不能首先放置 "^" 的模式?"^^abc$$" 是否可以?
1. 如果 "^" 和 "$" 已经是模式的第一个和最后一个字符,我们能避免复制吗?
1. 在多行模式下如何参与其中,如果有的话?
1. regexinstance.lastIndex 是正则表达式实例的可变部分(或在旧浏览器上的 RegExp 全局标志),它用作同一字符串上多次 exec() 调用的字符串偏移量。我毫无头绪,如果 re-* 获取设置全局标志的正则表达式时应该怎么办。(顺便说一下,这是拒绝 CLJS-150 的一个非常好的理由:允许 clojure 接受全局标志使正则表达式对象具有状态性,并完全破坏 re-seq 等。)

4 个答案

0 投票

评论者:favila

我想提出一个相当激进的建议,这将:修复此问题以及 CLJS-810,让我们更好地解决 CLJS-485、CLJS-746、CLJS-794(clojure.string/replace 痛点),并允许我们在 js 中将一些正则表达式作为值的魅力添加到模式中(CLJS-67 和 CLJS-68),并通过实现更多的 re-* 函数将 clojurescript 的正则表达式处理更接近 clojure。

在下面的 cljsfiddle 中的示例实现(不是补丁):http://cljsfiddle.net/fiddle/favila.regexp

关键点

  1. 创建一个Pattern对象,该对象由re-pattern创建,它提供了创建用于搜索(re-find)或精确匹配(re-matches)或重复搜索(re-seq,re-matcher + re-find)的正则表达式的方法。即使它们是相似的定期表达式字符串,JavaScript中每个这些都必须是一个不同的RegExp对象。re-find和re-matches模式可以被缓存。所有这些都可以懒惰地生成正则表达式。
  2. 正则表达式文字将产生这些Pattern对象而不是RegExp对象。
  3. 创建一个Matcher对象来对应尚未实现的re-matcher。它结合了带全局标志的正则表达式对象、搜索字符串和完成标志。如果它保持最后的匹配(类似于Java),Clojure Script也可以实现re-groups。
  4. 让re-seq使用Matcher对象,从而使用本地正则表达式提供的.lastIndex进行全局匹配。(其实现不再需要在每次匹配后进行字符串切片。)
  5. 如果re-find被一个本地RegExp对象而不是一个模式给出,它将完全使用它。这与当前行为相符。
  6. 如果re-matches被一个本地RegExp对象给出并且它不支持精确匹配,它将从输入RegExp克隆一个新RegExp并在前后加上^和$,并添加全局标志。(此技术在clojure.string/replace中已使用,但使用不正确。)

你认为呢?

0 投票

评论者:dnolen

这听起来很有趣,但我对互操故事有点担心。我认为人们会期待函数接受正则正则表达式以及Pattern。你没有在这里提到这个问题吗?

0 投票

评论者:favila

如果我的某些想法比较模糊,我也不能从长远的角度来考虑这个问题。

首先注意,一类狭窄的正则表达式实际上是“纯的”:如果它们执行全文匹配(例如,从^开始并以$结束)并且全局标志设置为false,则其lastIndex始终似乎为0。

互操可能性

  • Pattern和RegExp可以互相创建,因此强制转换始终是一个选项。例如,re-pattern可以接受RegExp和一些其他(Clojure-specific)函数可以强制从Pattern或Matcher转换为RegExp。(或者也许re-matcher可以返回一个RegExp兼容的对象--参见下文。)
  • 给Clojure Script的RegExp:“纯”正则表达式可以直接使用,否则我们将创建Pattern和/或Matcher。(我不记得细节了,但fiddle应该包括它们。)
  • 作为RegExp使用的Pattern:Pattern可以公开RegExp实例的所有属性。如果模式是纯的,它可以实现.test和.exec。.lastIndex始终为0。不清楚如何处理不纯的模式:抛出异常、仍按纯模式处理,还是返回新对象?
  • 作为RegExp使用的Matcher:一个Matcher可以精确地复制一个RegExp实例,甚至可能使用相同的原型。像RegExp一样使用它将改变对象并扰乱其内部状态,但只要它始终一致地作为RegExp或始终一致地作为Matcher使用,这就不会成问题。备注
    **** Matcher在Java中保留匹配的字符串。JavaScript信任你始终提供相同的字符串(例如,在while循环中)。
    • Java的Matcher保存最后一个匹配(用于re-groups)。JavaScript的RegExp不一定。
      当Javascript的RegExp的lastIndex达到源字符串的末尾时,它将自动重置。而Java的Matcher不会。 Matcher必须是一个包装器,而不是一个普通的RegExp,因为它有三个额外的状态。
      re-matcher的返回值只为re-find的1-arg形式和re-groups生成器所用。 re-seq可以在内部使用matcher,但由于它是私有的,因此不必这样做。
    • 应该实现Matcher的其他Java方法吗?
  • 传递给String.prototype.match、.replace、.search和.split的模式:我还没有考虑这个问题。考虑因素
    **** 任何使用通过模式字面量或re-pattern创建的对象,并将其作为参数传递给这些String方法的cljs代码都是问题代码。如果它们使用clojure.string方法,则不会有问题。
    • 这样的代码在java clojure中也是不可能的:只有(.split s "pattern-str")在java/clj和js/cljs中相同,并且它将继续在两个平台上工作(不使用标志)。我们可能只需要让人们修复这样的代码,因为它是平台相关的,但我需要看看这是否很普遍。
      这段代码的修复方法可以是:
      使用我们将提供的pattern→regexp转换函数。
      直接使用js/RegExp直接构造这些正则表达式。
      使用clojure.string函数代替String方法。这也具有在clj和cljs之间可移植的优点。
    • 我们可能可以通过修补RegExp构造函数或修改String原型链来自动执行模式→regexp转换,但这是一种激进的方法。

如果我没有错的话,在Clojure(java)代码中使用Pattern和Match方法或直接使用String方法的情况非常罕见。大部分情况下,它们被视为通过re-*和clojure.string/**使用的不可见对象。以相同风格编写的代码将不受影响,我们可以宣布任何其他使用为平台特定,并要求明确创建正则表达式(并且不必让Matcher或Pattern表现得像正则表达式)。这是我没有太多的RegExp.prototype.test、.exec和String.prototype.match、.replace、.search、.split使用时的Interop首选方法。

0 投票
通过
参考:https://clojure.atlassian.net/browse/CLJS-776(由favila报告)
...