【问题标题】:Explain this pattern matching code解释这个模式匹配代码
【发布时间】:2013-05-18 04:51:55
【问题描述】:

此代码来自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 魔法是如何工作的?

【问题讨论】:

标签: scala


【解决方案1】:

unapply 的一般工作原理如下:

当你这样做时

obj match {case Pattern(foo, bar) => ... }

Pattern.unapply(obj) 被调用。这可以返回None(在这种情况下模式匹配失败)或Some(x,y)(在这种情况下)foobar 绑定到xy

如果您使用Pattern(OtherPattern, YetAnotherPatter) 而不是Pattern(foo, bar),则x 将匹配OtherPattern 模式,y 将匹配YetAnotherPattern。如果所有这些模式匹配成功,则执行匹配主体,否则尝试下一个模式。

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


所以这里& 是一个总是返回Some(a,a) 的模式,不管a 是什么。所以& 总是匹配并将匹配的对象绑定到它的两个操作数。在代码中意味着

obj match {case x & y => ...}

将始终匹配,xy 将具有与 obj 相同的值。

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

即当你这样做时

obj match { case SomePattern & SomeOtherPattern => ...}`

首先应用模式&。正如我所说,它总是匹配并绑定obj 到它的LHS 和它的RHS。那么SomePattern 应用于& 的LHS(与obj 相同),SomeOtherPattern 应用于& 的RHS(也与obj 相同)。

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

【讨论】:

  • 我可以看到 & 的意图,但不是 & 的定义如何提供它。你能进一步解释一下吗?
  • @Paul:我想,我做到了。 & 模式接受一个对象并返回两次。这就是它所做的一切。我会尝试进一步解释一下,但这基本上就是全部了。
  • 是的,可能是我太密集了(而且是 Scala 新手)。如果您有我应该先阅读的内容的指针,请发布!
  • 好的,我想我现在有了。谢谢
【解决方案2】:

让我们从代码中执行此操作。首先,一个小的重写:

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"
}

看起来丑陋的代码,即使没有优化只在extractor11 不为空的情况下得到extractor12,并且没有应该在有注释的地方出现代码重复。所以我会用另一种风格来写它:

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"
)

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

【讨论】:

    【解决方案3】:

    有关更多信息,我建议阅读Scala Language Specification中缀操作模式部分 (8.1.10)。

    中缀操作模式p op q 是 构造函数的简写或 提取器模式op(p,q)。这 的优先级和关联性 模式中的运算符与 在表达式中。

    这几乎是全部内容,但是您可以阅读有关构造函数和提取器模式以及一般模式的信息。它有助于将语法糖方面(它的“魔术”部分)与相当简单的模式匹配概念分开:

    模式是由常量构建的, 构造函数、变量和类型 测试。模式匹配测试是否 给定值(或值序列) 具有由图案定义的形状, 如果是,则绑定变量 在对应的模式中 值的组成部分(或序列 值)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-13
      • 2010-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-29
      • 1970-01-01
      相关资源
      最近更新 更多