unapply和unapplySeq有什么区别?

44

为什么Scala同时拥有unapplyunapplySeq?它们之间有什么区别?我应该在什么情况下更倾向于使用其中一个?

3个回答

41

不详细深入地解释,简化一下:

对于常规参数,apply 构造函数和 unapply 解构函数会被使用:

object S {
  def apply(a: A):S = ... // makes a S from an A
  def unapply(s: S): Option[A] = ... // retrieve the A from the S
}
val s = S(a)
s match { case S(a) => a } 

对于重复的参数,apply构造函数和unapplySeq函数进行解构:

object M {
  def apply(a: A*): M = ......... // makes a M from an As.
  def unapplySeq(m: M): Option[Seq[A]] = ... // retrieve the As from the M
}
val m = M(a1, a2, a3)
m match { case M(a1, a2, a3) => ... } 
m match { case M(a, as @ _*) => ... } 
请注意,对于第二种情况,重复的参数会被视为一个Seq,且A*_*之间的相似性也是如此。
因此,如果您想要解构一个自然包含各种单一值的东西,请使用unapply。如果您想要解构一个包含Seq的东西,请使用unapplySeq

18

定长参数与可变参数。在《Scala 模式匹配》(pdf)中有很好的解释和对应的示例。我也在这个答案中提供了相应的示例。

简言之:

object Sorted {
  def unapply(xs: Seq[Int]) =
    if (xs == xs.sortWith(_ < _)) Some(xs) else None
}

object SortedSeq {
  def unapplySeq(xs: Seq[Int]) =
    if (xs == xs.sortWith(_ < _)) Some(xs) else None
}

scala> List(1,2,3,4) match { case Sorted(xs) => xs }
res0: Seq[Int] = List(1, 2, 3, 4)
scala> List(1,2,3,4) match { case SortedSeq(a, b, c, d) => List(a, b, c, d) }
res1: List[Int] = List(1, 2, 3, 4)
scala> List(1) match { case SortedSeq(a) => a }
res2: Int = 1

那么,您认为下面的示例展示了哪一个?

scala> List(1) match { case List(x) => x }
res3: Int = 1

啊,谢谢!这很有道理。从 Haskell 的角度来看,我认为匹配 List(a,b,c) 可以被解释为匹配 a :: b :: c :: Nil,但是似乎 Scala 并不这样做;相反,它使用了 ListunapplySeq 方法。 - Dan Burton
此答案中提到的论文可以在此处找到:http://www.michaelrueegg.name/static/papers/PatternMatchingInScala.pdf - Jaime Febres
1
@dan Scala可以按照您的期望在列表上进行匹配,方法是在::伴生对象上拥有一个unapply方法。正如预期的那样,这个unapply只是将列表(或者更确切地说是::)解构为头和尾。请注意,这仅适用于列表,而不适用于任何Seq。 - Jürgen Strobel
1
上述论文的URL似乎已经失效。现在可以在http://wiki.ifs.hsr.ch/SemProgAnTr/files/PatternMatchingInScala.pdf找到它。 - tossbyte

0

一些例子:

scala> val fruit = List("apples", "oranges", "pears")
fruit: List[String] = List(apples, oranges, pears)

scala> val List(a, b, c) = fruit
a: String = apples
b: String = oranges
c: String = pears

scala> val List(a, b, _*) = fruit
a: String = apples
b: String = oranges

scala> val List(a, _*) = fruit
a: String = apples

scala> val List(a,rest @ _*) = fruit
a: String = apples
rest: Seq[String] = List(oranges, pears)

scala> val a::b::c::Nil = fruit
a: String = apples
b: String = oranges
c: String = pears

scala> val a::b::rest = fruit
a: String = apples
b: String = oranges
rest: List[String] = List(pears)

scala> val a::rest = fruit
a: String = apples
rest: List[String] = List(oranges, pears)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接