为什么这种模式匹配在Scala中没有像预期的那样工作?

时间:2014-01-02 23:12:41

标签: scala pattern-matching

我正在尝试复制Joshua Suereth在2013年Devoxx的题为“如何在战壕中使用Scala”的演讲中提出的强大模式匹配示例。不幸的是,我无法实现他所描述的,我无法理解什么是错的。有人能给我一些暗示我缺少的东西吗? (我的Scala版本是2.10.3)

请参阅下面的自包含代码:

case class Person(name: String, residence: Seq[Residence])
case class Residence(city: String, country: String)

object LivesIn {
  def unapply(p: Person): Option[Seq[String]] =
    Some(
      for(r <- p.residence)
      yield r.city
    )
}

class StringSeqContains(value: String) {
  def unapply(in: Seq[String]): Boolean =
    in contains value
}

object PatternPower extends App {

  val people =
    Seq(Person("Emre", Seq(Residence("Antwerp", "BE"))),
      Person("Ergin", Seq(Residence("Istanbul", "TR"))))

  val Istanbul = new StringSeqContains("Istanbul")

  // #1 does not work as expected, WHY?
  println(
    people collect {
      case person @ LivesIn(Istanbul) => person
    }
  )

  // #2 works as expected
  println(
    people collect {
      case person @ LivesIn(cities) if cities.contains("Istanbul") => person
    }
  )

  // #3 works as expected
  println(
    people collect {
      case person @ Person(_, res) if res.contains(Residence("Istanbul", "TR")) => person
    }
  )

}

当我编译并运行它时,我得到:

List()
List(Person(Ergin,List(Residence(Istanbul,TR))))
List(Person(Ergin,List(Residence(Istanbul,TR))))

如源代码所示,我无法理解为什么第一个模式不会产生与其余两个模式匹配相同的结果。有什么想法吗?

2 个答案:

答案 0 :(得分:6)

你的LivesIn提取器需要一个Seq作为参数。

以下变体符合您的期望:

println(
  people collect {
    case person @ LivesIn(List("Istanbul")) => person
  }
)

答案 1 :(得分:1)

经过一番思考和谷歌搜索,我意识到应该将()添加到内部提取器中(感谢The Neophyte's Guide to Scala Part 1: Extractors)。

换句话说,以下工作符合预期:

people collect {
  case person @ LivesIn(Istanbul()) => person
}

而下面的代码默默地,没有任何抱怨,返回List()

people collect {
  case person @ LivesIn(Istanbul) => person
}

除非我以另一种方式弄错了(例如,有办法让它没有parantheses工作),我认为技术主持人应该更加小心代码片段/伪代码片段(以便一些好奇的观众将会不要失去不眠时; - )