此代码来自 Querying a Dataset with Scala's Pattern Matching :
object & { def unapply[A](a: A) = Some((a, a)) }
"Julie" match {
case Brothers(_) & Sisters(_) => "Julie has both brother(s) and sister(s)"
case Siblings(_) => "Julie's siblings are all the same sex"
case _ => "Julie has no siblings"
}
// => "Julie has both brother(s) and sister(s)"
&
如何实际运作?我没有在连词的任何地方看到布尔测试。这个Scala魔法是如何工作的?
答案 0 :(得分:29)
以下是unapply
的工作原理:
当你这样做时
obj match {case Pattern(foo, bar) => ... }
调用 Pattern.unapply(obj)
。这可以返回None
,在这种情况下模式匹配失败,或Some(x,y)
在这种情况下foo
和bar
绑定到x
和{{} 1}}。
如果您y
代替Pattern(foo, bar)
,那么Pattern(OtherPattern, YetAnotherPatter)
将与模式x
匹配,OtherPattern
将与y
匹配。如果所有这些模式匹配都成功,则执行匹配的主体,否则尝试下一个模式。
如果模式的名称不是字母数字,而是符号(如YetAnotherPattern
),则使用中缀,即您编写&
而不是foo & bar
。
所以这里&(foo, bar)
是一种模式,无论&
是什么,它总是返回Some(a,a)
。因此a
始终匹配并将匹配的对象绑定到其两个操作数。在代码中意味着
&
将始终匹配,obj match {case x & y => ...}
和x
都将与y
具有相同的值。
在上面的示例中,这用于将两个不同的模式应用于同一个对象。
即。当你做的时候
obj
首先应用模式obj match { case SomePattern & SomeOtherPattern => ...}`
。正如我所说,它总是匹配并&
绑定到其LHS及其RHS。因此,obj
应用于SomePattern
的LHS(与&
相同),obj
应用于SomeOtherPattern
的RHS(即也与&
)相同。
因此,实际上,您只是将两个模式应用于同一个对象。
答案 1 :(得分:7)
让我们从代码中做到这一点。首先,重写一次:
object & { def unapply[A](a: A) = Some(a, a) }
"Julie" match {
// case Brothers(_) & Sisters(_) => "Julie has both brother(s) and sister(s)"
case &(Brothers(_), Sisters(_)) => "Julie has both brother(s) and sister(s)"
case Siblings(_) => "Julie's siblings are all the same sex"
case _ => "Julie has no siblings"
}
新的重写意味着完全相同的事情。注释行使用中缀符号表示提取器,第二行使用常规表示法。他们都翻译成同样的东西。
因此,Scala会反复将“Julie”提供给提取器,直到所有未绑定的变量都分配给Some
为止。第一个提取器是&
,所以我们得到了这个:
&.unapply("Julie") == Some(("Julie", "Julie"))
我们回来了Some
,所以我们可以继续进行比赛。现在我们有两个元素的元组,我们在&
中也有两个提取器,所以我们将元组的每个元素提供给每个提取器:
Brothers.unapply("Julie") == ?
Sisters.unapply("Julie") == ?
如果这两个都返回Some
,那么匹配就会成功。只是为了好玩,让我们重写这段代码而不进行模式匹配:
val pattern = "Julie"
val extractor1 = &.unapply(pattern)
if (extractor1.nonEmpty && extractor1.get.isInstanceOf[Tuple2]) {
val extractor11 = Brothers.unapply(extractor1.get._1)
val extractor12 = Sisters.unapply(extractor1.get._2)
if (extractor11.nonEmpty && extractor12.nonEmpty) {
"Julie has both brother(s) and sister(s)"
} else {
"Test Siblings and default case, but I'll skip it here to avoid repetition"
}
} else {
val extractor2 = Siblings.unapply(pattern)
if (extractor2.nonEmpty) {
"Julie's siblings are all the same sex"
} else {
"Julie has no siblings"
}
丑陋的代码,即使没有优化也只有extractor12
如果extractor11
不为空,并且没有代码重复,应该已经去了评论。所以我会用另一种风格写出来:
val pattern = "Julie"
& unapply pattern filter (_.isInstanceOf[Tuple2]) flatMap { pattern1 =>
Brothers unapply pattern1._1 flatMap { _ =>
Sisters unapply pattern1._2 flatMap { _ =>
"Julie has both brother(s) and sister(s)"
}
}
} getOrElse {
Siblings unapply pattern map { _ =>
"Julie's siblings are all the same sex"
} getOrElse {
"Julie has no siblings"
}
}
一开始flatMap
/ map
的模式提示了另一种写作方式:
val pattern = "Julie"
(
for {
pattern1 <- & unapply pattern
if pattern1.isInstanceOf[Tuple2]
_ <- Brothers unapply pattern1._1
_ <- Sisters unapply pattern1._2
} yield "Julie has both brother(s) and sister(s)
) getOrElse (
for {
_ <- Siblings unapply pattern
} yield "Julie's siblings are all the same sex"
) getOrElse (
"julie has no siblings"
)
您应该能够运行所有这些代码并亲自查看结果。
答案 2 :(得分:1)
有关其他信息,建议您阅读Scala Language Specification的中缀操作模式部分(8.1.10)。
中缀操作模式
p op q
是一个 构造函数的简写或 提取器模式op(p,q)
。该 的优先级和相关性 模式中的运算符与 在表达式中。
这几乎就是它的全部内容,但是你可以阅读一般的构造函数和提取器模式和模式。它有助于将语法糖方面(它的“神奇”部分)与模式匹配的相当简单的概念分开:
模式是由常量构建的, 构造函数,变量和类型 试验。模式匹配测试是否 给定值(或值序列) 具有由图案定义的形状, 如果是,则绑定变量 在模式中对应 值的组成部分(或序列 价值观)。