正则表达式模式相等性

6
在ScalaTest中,我有以下检查:
"abc".r shouldBe "abc".r

但它并不相等。我不明白。
abc was not equal to abc
ScalaTestFailureLocation: com.ing.cybrct.flink.clickstream.ConfigsTest$$anonfun$6 at (ConfigsTest.scala:97)
Expected :abc
Actual   :abc

1
在REPL中,"abc".r == "abc".r也是false,因此它不是特定于scalatest的。 - joel
3个回答

10

虽然可以确定两个正则表达式是否接受相同的语言, 但这似乎非常复杂,而且对于日常正则表达式使用来说并不是特别有用。因此,编译后的正则表达式模式的相等性只是引用相等性:

val x = "abc".r
val y = "abc".r
x == y
// res0: Boolean = false

那么在scalatest中,我们无法测试两个正则表达式是否相等吗? - fluency03
我们能这样做吗:"abc".r.toString() shouldBe "abc".r.toString() - fluency03
然而,在Scala中,至少在JVM上,“==”仅仅是调用了equals方法,这并不是指引用相等性。 - fluency03
2
@fluency03,你可以直接询问 x.regex == y.regex 而不是调用 toString() 方法,因为 .regex 已经包含了从编译模式生成的字符串表示。这将会是一个“低成本”的内在相等,以某种程度上近似于“有趣”的外延相等。对于正则表达式,equals 方法只是重定向到引用相等的 eq 方法。 - Andrey Tyukin

5

在 Scalatest 3.0.5 中,方法 shouldBe 将等式检查委托给方法 areEqualComparingArraysStructurally

def shouldBe(right: Any): Assertion = {
  if (!areEqualComparingArraysStructurally(leftSideValue, right)) {
    val (leftee, rightee) = Suite.getObjectsForFailureMessage(leftSideValue, right)
    val localPrettifier = prettifier // Grabbing a local copy so we don't attempt to serialize AnyShouldWrapper (since first param to indicateFailure is a by-name)
    indicateFailure(FailureMessages.wasNotEqualTo(localPrettifier, leftee, rightee), None, pos)
  }
  else indicateSuccess(FailureMessages.wasEqualTo(prettifier, leftSideValue, right))
}

这将简单地将相等性检查(正如你所期望的)委派给 == 运算符:

private[scalatest] def areEqualComparingArraysStructurally(left: Any, right: Any): Boolean = {
  // Prior to 2.0 this only called .deep if both sides were arrays. Loosened it
  // when nearing 2.0.M6 to call .deep if either left or right side is an array.
  // TODO: this is the same algo as in scalactic.DefaultEquality. Put that one in
  // a singleton and use it in both places.
  left match {
    case leftArray: Array[_] =>
      right match {
        case rightArray: Array[_] => leftArray.deep == rightArray.deep
        case _ => leftArray.deep == right
      }
    case _ => {
      right match {
        case rightArray: Array[_] => left == rightArray.deep
        case _ => left == right
      }
    }
  }
}

在Scala中,至少在JVM上,==只是调用equals方法,如果没有被覆盖,则检查比较的变量是否指向同一个对象。case class是特殊的,因为编译器会为你覆盖equals方法以比较构造函数的参数。
您可以使用以下代码轻松测试它(但是您可以想象,对于自己使用==也是一样的):
package org.example

import org.scalatest.{FlatSpec, Matchers}

final class MyClass(val a: Int)

final case class MyCaseClass(a: Int)

final class TestSpec extends FlatSpec with Matchers {

  "equality on case classes" should "succeed" in {

    new MyCaseClass(1) shouldBe new MyCaseClass(1)

  }

  "equality on non-case classes" should "fail" in {

    new MyClass(1) shouldNot be(new MyClass(1))

  }

  "equality between Regex objects" should "fail" in {

    "abc".r shouldNot be("abc".r)

  }

}

r 方法所做的是实例化一个新的 Regex 对象,这不是一个 case class 并且也没有覆盖等式定义,因此导致了您看到的结果。


1
那就是我想说的,但我看到我的解释很冗长,我会修正的,谢谢。 - stefanobaghino
也许是“字面上相同的内存块”之类的?“字面上相同”仍然有些含糊不清,因为它通过同样未定义的“相同性”概念来定义尚未定义的“平等”概念。此外,“字面上”的词汇可能会引发这样的印象,即描述对象的代码的句法形式更重要,而不是其内存位置。这就是为什么我喜欢短语“引用相等性”,至少这是一件在全球范围内更为普遍可理解的事情。我知道我很挑剔。 ;) - Andrey Tyukin
1
希望这样做:比较的变量指向同一个对象。 - stefanobaghino
那么在scalatest中,我们无法测试两个正则表达式是否相等吗? - fluency03
@fluency03 只需将正则表达式转换回字符串即可。请注意,两个正则表达式可能表示相同的模式,但它们的字符串表示可能不相等。 - Michael Mior

-1

是的,它们不相等。 "abc".r 都是指向两个不同内存位置的不同引用。 shouldbe 是为了检查相等性而不是身份


1
这并不完全正确。例如,val (a,b)=(Some(1), Some(1))也是两个不同的引用,但是a == b为真... - Dima
"abc".r == "abc".r ...它会返回false。 - Balaji Reddy
没错。在这两种情况下,它们是不同的内存位置,但一个结果是真的,而另一个结果是假的。 - Dima

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接