使用字符串插值的模式匹配
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) 使用 A
和 B
的 unapply
方法和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:为了说明,与使用 apply
和 unapply
的案例 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 个部分。
""
""
"my_a__myB"
同样,第 1 部分未通过 unapply()
,其他部分从未尝试过。
虽然您的 Edit 3 代码在技术上是正确的,但我并不觉得它很有说服力。您只是证明了 (String,String)
=>AB(String,String)
=>(String,String)
是(或可以是)无损数据转换。 (String,String)
=>String
不能这样说,它引入了一些歧义,即足以保证恢复原始数据的信息丢失。这种损失是转换本身固有的,而不是用于实现它的工具(插值)。
事实上 case class
和 String
插值都使用 apply()
/unapply()
在幕后让我觉得无关紧要。
在以下使用 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) 使用 A
和 B
的 unapply
方法和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:为了说明,与使用 apply
和 unapply
的案例 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 个部分。
""
""
"my_a__myB"
同样,第 1 部分未通过 unapply()
,其他部分从未尝试过。
虽然您的 Edit 3 代码在技术上是正确的,但我并不觉得它很有说服力。您只是证明了 (String,String)
=>AB(String,String)
=>(String,String)
是(或可以是)无损数据转换。 (String,String)
=>String
不能这样说,它引入了一些歧义,即足以保证恢复原始数据的信息丢失。这种损失是转换本身固有的,而不是用于实现它的工具(插值)。
事实上 case class
和 String
插值都使用 apply()
/unapply()
在幕后让我觉得无关紧要。