使用字符串插值的模式匹配

Pattern matching using string interpolation

在以下使用 Scala 2.13.3 的示例中,第一个模式匹配,但第二个不匹配。

第三个模式再次匹配,而第四个不匹配(请注意,第四个匹配表达式中的 separator 包含在反引号中,因此引用之前定义的值)。

  trait A
  case object A extends A {
    def unapply(a: String): Option[A] = if (a == "my_a") Some(A) else None
  }
  trait B
  case object B extends B {
    def unapply(b: String): Option[B] = if (b == "myB") Some(B) else None
  }

  val match1 = "myB_my_a" match {
    case s"${B(b)}_${A(a)}" => Some((a,b))
    case _ => None
  } // Some((A,B))

  val match2 = "my_a_myB" match {
    case s"${A(a)}_${B(b)}" => Some((a,b))
    case _ => None
  } // None

  val match3 = "my_a__myB" match {
    case s"${A(a)}__${B(b)}" => Some((a,b))
    case _ => None
  } // Some((A,B))

  val separator = "__"
  val match4 = s"my_a${separator}myB" match {
    case s"${A(a)}${`separator`}${B(b)}" => Some((a,b))
    case _ => None
  } // None

为什么只有第一个和第三个模式匹配?

是否有一个很好的 匹配 替代第二个模式,a) 使用 ABunapply 方法和b) 我们不知道这些方法接受什么字符串?

编辑 1:添加案例对象 B 和另一个匹配示例。

编辑 2:另一个例子来说明 jwvh 的回答:

  val (a, b) = ("my_a", "myB")
  val match5 = s"${a}_${b}" match {
    case s"${`a`}_${`b`}" => Some((a, b)) // does not match
    case s"${x}_${y}" => Some((x, y)) // matches: Some(("my", "a_myB"))
  }

编辑 3:为了说明,与使用 applyunapply 的案例 class 构造和提取不同,构造和提取使用类似字符串插值的字符串不是(也不可能是)反函数:

  case class AB(a: String, b: String)
  val id = (AB.apply _ tupled) andThen AB.unapply andThen (_.get)
  val compare = id(("my_a", "myB")) == ("my_a", "myB") // true

  val construct: (String, String) => String = (a,b) => s"${a}_${b}"
  val extract: String => (String, String) = { case s"${a}_${b}" => (a,b) }
  val id2 = (construct tupled) andThen extract
  val compare2 = id2(("my_a","myB")) == ("my_a","myB") // false

正如您自己的测试(在评论中提到)所展示的,插值器识别出匹配模式 "${A(a)}_${B(b)}" 由下划线 _ 分隔的两部分组成。因此,会做出最佳猜测来相应地拆分目标字符串。

第 1 部分 "my" 被发送到失败的 A.unapply()。第二部分 "a_myB" 甚至没有尝试过。

类似的事情发生在 match4。模式 "${A(a)}${'separator'}${B(b)}" 有 3 个美元符号,因此有 3 个部分。但是,没有任何明确的字符来锚定模式,目标字符串被分成这 3 个部分。

  1. ""
  2. ""
  3. "my_a__myB"

同样,第 1 部分未通过 unapply(),其他部分从未尝试过。


虽然您的 Edit 3 代码在技术上是正确的,但我并不觉得它很有说服力。您只是证明了 (String,String)=>AB(String,String)=>(String,String) 是(或可以是)无损数据转换。 (String,String)=>String 不能这样说,它引入了一些歧义,即足以保证恢复原始数据的信息丢失。这种损失是转换本身固有的,而不是用于实现它的工具(插值)。

事实上 case classString 插值都使用 apply()/unapply() 在幕后让我觉得无关紧要。