首页 > 解决方案 > 使用字符串插值进行模式匹配

问题描述

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

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

  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) 使用的unapply方法AB在哪里 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:为了说明,与使用applyand的案例类构造和提取不同unapply,使用类似字符串插值的字符串的构造和提取不是(也不能是)反函数:

  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

标签: scalapattern-matchingstring-interpolation

解决方案


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

第一部分,"my",被发送到A.unapply()它失败的地方。"a_myB"甚至没有尝试第二部分。

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

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

同样,第一部分失败了unapply(),其他部分从未尝试过。


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

case classString插值都使用apply()/unapply()在引擎盖下的事实让我觉得无关紧要。


推荐阅读