此代码来自使用Scala的模式匹配查询数据集:
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)"
Run Code Online (Sandbox Code Playgroud)
&实际上如何运作?我没有在连词的任何地方看到布尔测试.这个Scala魔法是如何工作的?
sep*_*p2k 29
以下是unapply一般的工作方式:
当你这样做
obj match {case Pattern(foo, bar) => ... }
Run Code Online (Sandbox Code Playgroud)
Pattern.unapply(obj)叫做.这可以返回,None在这种情况下,模式匹配是失败,或者Some(x,y)在这种情况下foo,bar并且绑定到x和y.
如果不是Pattern(foo, bar)你做的Pattern(OtherPattern, YetAnotherPatter)话,那么x就会与模式相匹配OtherPattern并y与之匹配YetAnotherPattern.如果所有这些模式匹配都成功,则执行匹配的主体,否则尝试下一个模式.
当模式的名称不是字母数字,而是符号(如&)时,它使用中缀,即你写foo & bar而不是&(foo, bar).
所以这里&的模式总是会返回,Some(a,a)无论是什么a.因此,&始终匹配并将匹配的对象绑定到其两个操作数.在代码中意味着
obj match {case x & y => ...}
Run Code Online (Sandbox Code Playgroud)
总是会匹配,并且都x与y具有相同的价值obj.
在上面的示例中,这用于将两个不同的模式应用于同一对象.
即,当你这样做
obj match { case SomePattern & SomeOtherPattern => ...}`
Run Code Online (Sandbox Code Playgroud)
首先&应用模式.正如我所说,它始终obj与其LHS及其RHS 相匹配.那么SomePattern应用于&LHS(与之相同obj)并SomeOtherPattern应用于&RHS(也与之相同obj).
所以实际上,您只是将两个模式应用于同一个对象.
我们从代码中做到这一点.首先,小改写:
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"
}
Run Code Online (Sandbox Code Playgroud)
新的重写意味着完全相同的事情.注释行使用中缀符号表示提取器,第二行使用常规表示法.他们都翻译成同样的东西.
因此,Scala会反复将"Julie"提供给提取器,直到将所有未绑定的变量分配给Something.第一个提取器是&,所以我们得到这个:
&.unapply("Julie") == Some(("Julie", "Julie"))
Run Code Online (Sandbox Code Playgroud)
我们Some回来了,所以我们可以继续进行比赛.现在我们有两个元素的元组,我们内部也有两个提取器&,所以我们将元组的每个元素提供给每个提取器:
Brothers.unapply("Julie") == ?
Sisters.unapply("Julie") == ?
Run Code Online (Sandbox Code Playgroud)
如果这两个都返回了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"
}
Run Code Online (Sandbox Code Playgroud)
丑陋的代码,即使没有优化,只得到extractor12if 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"
}
}
Run Code Online (Sandbox Code Playgroud)
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"
)
Run Code Online (Sandbox Code Playgroud)
您应该能够运行所有这些代码并亲自查看结果.