解释这种模式匹配代码

Jim*_*Jim 19 scala

此代码来自使用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并且绑定到xy.

如果不是Pattern(foo, bar)你做的Pattern(OtherPattern, YetAnotherPatter)话,那么x就会与模式相匹配OtherPatterny与之匹配YetAnotherPattern.如果所有这些模式匹配都成功,则执行匹配的主体,否则尝试下一个模式.

当模式的名称不是字母数字,而是符号(如&)时,它使用中缀,即你写foo & bar而不是&(foo, bar).


所以这里&的模式总是会返回,Some(a,a)无论是什么a.因此,&始终匹配并将匹配的对象绑定到其两个操作数.在代码中意味着

obj match {case x & y => ...}
Run Code Online (Sandbox Code Playgroud)

总是会匹配,并且都xy具有相同的价值obj.

在上面的示例中,这用于将两个不同的模式应用于同一对象.

即,当你这样做

obj match { case SomePattern & SomeOtherPattern => ...}`
Run Code Online (Sandbox Code Playgroud)

首先&应用模式.正如我所说,它始终obj与其LHS及其RHS 相匹配.那么SomePattern应用于&LHS(与之相同obj)并SomeOtherPattern应用于&RHS(也与之相同obj).

所以实际上,您只是将两个模式应用于同一个对象.


Dan*_*ral 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"
}
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)

您应该能够运行所有这些代码并亲自查看结果.