模式匹配

模式

  Pattern         ::=  Pattern1 { ‘|’ Pattern1 }
  Pattern1        ::=  boundvarid ‘:’ TypePat
                    |  ‘_’ ‘:’ TypePat
                    |  Pattern2
  Pattern2        ::=  id [‘@’ Pattern3]
                    |  Pattern3
  Pattern3        ::=  SimplePattern
                    |  SimplePattern {id [nl] SimplePattern}
  SimplePattern   ::=  ‘_’
                    |  varid
                    |  Literal
                    |  StableId
                    |  StableId ‘(’ [Patterns] ‘)’
                    |  StableId ‘(’ [Patterns ‘,’] [id ‘@’] ‘_’ ‘*’ ‘)’
                    |  ‘(’ [Patterns] ‘)’
                    |  XmlPattern
  Patterns        ::=  Pattern {‘,’ Patterns}

模式由常量、构造函数、变量和类型测试构成。模式匹配测试给定值(或值序列)是否具有模式定义的形状,如果匹配,则将模式中的变量绑定到值的对应组件(或值序列)。同一个变量名在一个模式中不能被绑定多次。

示例

以下是一些模式示例:

  1. 模式 ex: IOException 匹配 IOException 类的所有实例,将变量 ex 绑定到该实例。
  2. 模式 Some(x) 匹配 Some(´v´) 形式的值,将 x 绑定到 Some 构造函数的参数值 ´v´。
  3. 模式 (x, _) 匹配值对,将 x 绑定到对的第一个组件。第二个组件与通配符模式匹配。
  4. 模式 x :: y :: xs 匹配长度为 ´\geq 2´ 的列表,将 x 绑定到列表的第一个元素,y 绑定到列表的第二个元素,xs 绑定到剩余部分。
  5. 模式 1 | 2 | 3 匹配 1 到 3 之间的整数。

模式匹配始终在一个提供模式预期类型的上下文中进行。我们区分以下几种模式。

变量模式

  SimplePattern   ::=  ‘_’
                    |  varid

变量模式 ´x´ 是一个简单的标识符,以小写字母开头。它匹配任何值,并将变量名绑定到该值。´x´ 的类型是来自外部的模式预期类型。一个特例是通配符模式 _,它在每次出现时都被视为一个新的变量。

类型模式

  Pattern1        ::=  varid ‘:’ TypePat
                    |  ‘_’ ‘:’ TypePat

一个类型模式 ´x: T´ 由一个模式变量 ´x´ 和一个类型模式 ´T´ 组成。 ´x´ 的类型是类型模式 ´T´,其中每个类型变量和通配符都被替换为一个新的、未知的类型。此模式匹配任何被类型模式 ´T´ 匹配的值;它将变量名绑定到该值。

模式绑定器

  Pattern2        ::=  varid ‘@’ Pattern3

一个模式绑定器 ´x´@´p´ 由一个模式变量 ´x´ 和一个模式 ´p´ 组成。 变量 ´x´ 的类型是模式 ´p´ 隐含的静态类型 ´T´。 此模式匹配任何被模式 ´p´ 匹配的值 ´v´,并将变量名绑定到该值。

如果模式仅匹配类型 ´T´ 的值,则模式 ´p´ 隐含 类型 ´T´。

字面量模式

  SimplePattern   ::=  Literal

一个字面量模式 ´L´ 匹配任何与字面量 ´L´ 相等(在 == 方面)的值。 ´L´ 的类型必须符合模式的预期类型。

插值字符串模式

  Literal  ::=  interpolatedString

模式中插值字符串字面量的扩展与表达式中的相同。 如果它出现在模式中,则以下两种形式的插值字符串字面量

id"text0{ pat1 }text1 ... { patn }textn"
id"""text0{ pat1 }text1 ... { patn }textn"""

等效于

StringContext("""text0""", ..., """textn""").id(pat1, ..., patn)

您可以定义自己的 StringContext 来覆盖 scala 包中的默认 StringContext

如果成员 id 评估为一个提取器对象,则此扩展是类型正确的。 如果提取器对象具有 apply 以及 unapplyunapplySeq 方法,则处理后的字符串可以用作表达式或模式。

以 XML 为例

implicit class XMLinterpolation(s: StringContext) = {
    object xml {
        def apply(exprs: Any*) =
            // parse ‘s’ and build an XML tree with ‘exprs’
            //in the holes
        def unapplySeq(xml: Node): Option[Seq[Node]] =
          // match `s’ against `xml’ tree and produce
          //subtrees in holes
    }
}

然后,XML 模式匹配可以这样表达

case xml"""
      <body>
        <a href = "some link"> $linktext </a>
      </body>
     """ => ...

其中 linktext 是由模式绑定的变量。

稳定标识符模式

  SimplePattern   ::=  StableId

一个稳定标识符模式 是一个稳定标识符 ´r´。 ´r´ 的类型必须符合模式的预期类型。 该模式匹配任何值 ´v´,使得 ´r´ == ´v´(见这里)。

为了解决与变量模式的语法重叠,稳定标识符模式不能是简单名称,该名称以小写字母开头。 但是,可以将这样的变量名括在反引号中;然后它被视为一个稳定标识符模式。

示例

考虑以下类定义

class C { c =>
  val x = 42
  val y = 27
  val Z = 8
  def f(x: Int) = x match {
    case c.x => 1  // matches 42
    case `y` => 2  // matches 27
    case Z   => 3  // matches 8
    case x   => 4  // matches any value
  }
}

这里,前三个模式是稳定标识符模式,而最后一个是变量模式。

构造函数模式

SimplePattern   ::=  StableId ‘(’ [Patterns] ‘)’

一个构造函数模式的形式为 ´c(p_1, ..., p_n)´,其中 ´n \geq 0´。它由一个稳定标识符 ´c´ 组成,后面跟着元素模式 ´p_1, ..., p_n´。构造函数 ´c´ 是一个简单或限定名称,它表示一个 case 类。如果 case 类是单态的,那么它必须符合模式的预期类型,并且 ´x´ 的 主构造函数 的形式参数类型被视为元素模式 ´p_1, ..., p_n´ 的预期类型。如果 case 类是多态的,那么它的类型参数将被实例化,以便 ´c´ 的实例化符合模式的预期类型。然后,´c´ 的主构造函数的实例化形式参数类型被视为组件模式 ´p_1, ..., p_n´ 的预期类型。该模式匹配所有从构造函数调用 ´c(v_1, ..., v_n)´ 创建的对象,其中每个元素模式 ´p_i´ 匹配相应的值 ´v_i´。

当 ´c´ 的形式参数类型以重复参数结尾时,会出现一种特殊情况。这将在 这里 进一步讨论。

元组模式

  SimplePattern   ::=  ‘(’ [Patterns] ‘)’

一个元组模式 (´p_1´, ..., ´p_n´),其中 ´n \geq 2´ 等价于 ´p_1´ *: ... *: ´p_n´ *: scala.EmptyTuple

备注

提取器模式

  SimplePattern   ::=  StableId ‘(’ [Patterns] ‘)’

一个提取器模式 ´x(p_1, ..., p_n)´,其中 ´n \geq 0´,与构造函数模式具有相同的语法形式。但是,它不是 case 类,而是稳定标识符 ´x´ 表示一个对象,该对象具有名为 unapplyunapplySeq 的成员方法,该方法与模式匹配。

提取器模式不能匹配值 null。实现确保 unapply/unapplySeq 方法不会应用于 null

如果一个类型具有一个返回值类型为 Tget 方法和一个返回值类型符合 BooleanisEmpty 方法,则称该类型为类型 T提取器类型Option[T] 是类型 T 的提取器类型。

对象 ´x´ 中的 unapply 方法匹配模式 ´x(p_1, ..., p_n)´,如果它只有一个参数(以及可选的隐式参数列表),并且满足以下条件之一:

对象 ´x´ 中的 unapplySeq 方法匹配模式 ´x(q_1, ..., q_m, p_1, ..., p_n)´,如果它只接受一个参数,并且它的结果类型为 Option[(´T_1, ..., T_m´, Seq[S])](如果 m = 0,则也接受类型 Option[Seq[S]])。这种情况将在 下面 进一步讨论。

示例 1

如果我们定义一个提取器对象 Pair

object Pair {
  def apply[A, B](x: A, y: B) = Tuple2(x, y)
  def unapply[A, B](x: Tuple2[A, B]): Option[Tuple2[A, B]] = Some(x)
}

这意味着名称 Pair 可以用于代替 Tuple2 来进行元组的形成以及在模式中对元组进行解构。因此,以下操作是可能的

val x = (1, 2)
val y = x match {
  case Pair(i, s) => Pair(s + i, i * i)
}
示例 2

如果我们定义一个类 NameBased

class NameBased[A, B](a: A, b: B) {
  def isEmpty = false
  def get = this
  def _1 = a
  def _2 = b
}

那么 NameBased 本身就是一个 NameBased 的提取器类型,因为它有一个名为 isEmpty 的成员,返回一个布尔类型的值,并且它有一个名为 get 的成员,返回一个 NameBased 类型的的值。

由于它还具有 _1_2 成员,因此它可以在 n = 2 的提取器模式中使用,如下所示

object Extractor {
  def unapply(x: Any) = new NameBased(1, "two")
}

"anything" match {
  case Extractor(a, b) => println(s"\$a, \$b") //prints "1, two"
}

模式序列

SimplePattern ::= StableId ‘(’ [Patterns ‘,’] [varid ‘@’] ‘_’ ‘*’ ‘)’

模式序列 ´p_1, ..., p_n´ 出现在两种情况下。首先,在构造函数模式 ´c(q_1, ..., q_m, p_1, ..., p_n)´ 中,其中 ´c´ 是一个具有 ´m+1´ 个主构造函数参数的案例类,以一个 重复参数 结束,该参数的类型为 S*。其次,在提取器模式 ´x(q_1, ..., q_m, p_1, ..., p_n)´ 中,如果提取器对象 ´x´ 没有 unapply 方法,但它定义了一个 unapplySeq 方法,其结果类型是类型 (T_1, ... , T_m, Seq[S]) 的提取器类型(如果 m = 0,则类型 Seq[S] 的提取器类型也被接受)。模式 ´p_i´ 的预期类型为 ´S´。

模式序列中的最后一个模式可以是序列通配符 _*。每个元素模式 ´p_i´ 都使用 ´S´ 作为预期类型进行类型检查,除非它是序列通配符。如果存在最终的序列通配符,则模式匹配所有以匹配模式 ´p_1, ..., p_{n-1}´ 的元素开头的序列值 ´v´。如果没有给出最终的序列通配符,则模式匹配所有长度为 ´n´ 的序列值 ´v´,这些序列值由匹配模式 ´p_1, ..., p_n´ 的元素组成。

中缀操作模式

  Pattern3  ::=  SimplePattern {id [nl] SimplePattern}

中缀操作模式 ´p;\mathit{op};q´ 是构造函数或提取器模式 ´\mathit{op}(p, q)´ 的简写。模式中运算符的优先级和结合性与 表达式 中的相同。

中缀操作模式 ´p;\mathit{op};(q_1, ..., q_n)´ 是构造函数或提取器模式 ´\mathit{op}(p, q_1, ..., q_n)´ 的简写。

模式备选

  Pattern   ::=  Pattern1 { ‘|’ Pattern1 }

一个模式备选´p_1´ | ... | ´p_n´ 由多个备选模式 ´p_i´ 组成。所有备选模式都将使用模式的预期类型进行类型检查。它们不能绑定除通配符以外的变量。如果至少一个备选模式匹配值 ´v´,则备选模式匹配值 ´v´。

XML 模式

XML 模式在这里进行处理。

正则表达式模式

从 Scala 2.0 版本开始,正则表达式模式已在 Scala 中弃用。

Scala 的更高版本提供了简化版的正则表达式模式,涵盖了大多数非文本序列处理场景。序列模式是一种模式,它位于以下位置之一:(1)预期类型为 T 的模式,该类型符合 Seq[A](其中 A 为某个类型),或(2)具有迭代形式参数 A* 的案例类构造函数。最右侧位置的通配符星号模式 _* 代表任意长度的序列。它可以使用 @ 绑定到变量,如往常一样,在这种情况下,变量的类型将为 Seq[A]

不可反驳模式

如果以下情况之一适用,则模式 ´p´ 对于类型 ´T´ 是不可反驳

  1. ´p´ 是一个变量模式,
  2. ´p´ 是一个类型化模式 ´x: T'´,并且 ´T <: T'´,
  3. ´p´ 是一个构造函数模式 ´c(p_1, ..., p_n)´,类型 ´T´ 是类 ´c´ 的实例,类型 ´T´ 的主构造函数 具有参数类型 ´T_1, ..., T_n´,并且每个 ´p_i´ 对于 ´T_i´ 都是不可反驳的。
  4. ´p´ 是一个提取器模式,其中提取器类型为 Some[´T´](其中 ´T´ 为某个类型)
  5. ´p´ 是一个提取器模式,其中提取器类型 isEmpty 方法为单例类型 false
  6. ´p´ 是一个提取器模式,其中返回类型为单例类型 true

类型模式

  TypePat           ::=  Type

类型模式由类型、类型变量和通配符组成。类型模式 ´T´ 具有以下形式之一

底部类型 scala.Nothingscala.Null 不能用作类型模式,因为它们在任何情况下都不会匹配任何内容。

不属于上述形式之一的类型也被接受为类型模式。但是,此类类型模式将被转换为其 擦除。Scala 编译器将为这些模式发出“未经检查”警告,以标记可能发生的类型安全丢失。

类型变量模式 是一个以小写字母开头的简单标识符。

模式中的类型参数推断

类型参数推断是为带类型模式或构造函数模式中的绑定类型变量查找边界的过程。推断会考虑模式的预期类型。

带类型模式的类型参数推断

假设一个带类型模式 ´p: T'´。令 ´T´ 来自 ´T'´,其中 ´T'´ 中的所有通配符都被重命名为新的变量名。令 ´a_1, ..., a_n´ 为 ´T´ 中的类型变量。这些类型变量被认为在模式中是绑定的。令模式的预期类型为 ´\mathit{pt}´。

类型参数推断首先构造一个关于类型变量 ´a_i´ 的子类型约束集。初始约束集 ´\mathcal{C}_0´ 仅反映这些类型变量的边界。也就是说,假设 ´T´ 具有绑定类型变量 ´a_1, ..., a_n´,它们对应于类类型参数 ´a_1', ..., a_n'´,具有下界 ´L_1, ..., L_n´ 和上界 ´U_1, ..., U_n´,´\mathcal{C}_0´ 包含以下约束

$$ \begin{cases} a_i &<: \sigma U_i & \quad (i = 1, ..., n) \\ \sigma L_i &<: a_i & \quad (i = 1, ..., n) \end{cases} $$

其中 ´\sigma´ 是替换 ´[a_1' := a_1, ..., a_n' :=a_n]´。

然后通过进一步的子类型约束来扩充集合 ´\mathcal{C}_0´。有两种情况。

情况 1

如果存在一个关于类型变量 `a_1, ..., a_n` 的替换 `\sigma`,使得 `\sigma T` 符合 `\mathit{pt}`,则确定关于类型变量 `a_1, ..., a_n` 的最弱子类型约束 `\mathcal{C}_1`,使得 `\mathcal{C}_0 \wedge \mathcal{C}_1` 意味着 `T` 符合 `\mathit{pt}`。

情况 2

否则,如果 `T` 无法通过实例化其类型变量来使其符合 `\mathit{pt}`,则确定 `\mathit{pt}` 中所有定义为包含该模式的方法的类型参数的类型变量。令这些类型参数的集合为 `b_1 , ..., b_m`。令 `\mathcal{C}_0'` 为反映类型变量 `b_i` 范围的子类型约束。如果 `T` 表示最终类的实例类型,则令 `\mathcal{C}_2` 为关于类型变量 `a_1, ..., a_n` 和 `b_1, ..., b_m` 的最弱子类型约束集,使得 `\mathcal{C}_0 \wedge \mathcal{C}_0' \wedge \mathcal{C}_2` 意味着 `T` 符合 `\mathit{pt}`。如果 `T` 不表示最终类的实例类型,则令 `\mathcal{C}_2` 为关于类型变量 `a_1, ..., a_n` 和 `b_1, ..., b_m` 的最弱子类型约束集,使得 `\mathcal{C}_0 \wedge \mathcal{C}_0' \wedge \mathcal{C}_2` 意味着可以构造一个同时符合 `T` 和 `\mathit{pt}` 的类型 `T'`。如果不存在满足此属性的约束集 `\mathcal{C}_2`,则为静态错误。

最后一步是为类型变量选择类型边界,以暗示已建立的约束系统。对于上述两种情况,该过程有所不同。

情况 1

我们取 `a_i >: L_i <: U_i`,其中每个 `L_i` 都是最小的,每个 `U_i` 都是最大的,相对于 `<:`,使得 `a_i >: L_i <: U_i` 对于 `i = 1, ..., n` 意味着 `\mathcal{C}_0 \wedge \mathcal{C}_1`。

情况 2

我们取 `a_i >: L_i <: U_i` 和 `b_i >: L_i' <: U_i'`,其中每个 `L_i` 和 `L_j'` 都是最小的,每个 `U_i` 和 `U_j'` 都是最大的,使得 `a_i >: L_i <: U_i` 对于 `i = 1, ..., n` 和 `b_j >: L_j' <: U_j'` 对于 `j = 1, ..., m` 意味着 `\mathcal{C}_0 \wedge \mathcal{C}_0' \wedge \mathcal{C}_2`。

在这两种情况下,都允许局部类型推断来限制推断边界的复杂性。类型的最小性和最大性必须相对于可接受复杂度的类型集来理解。

构造函数模式的类型参数推断

假设一个构造函数模式 `C(p_1, ..., p_n)`,其中类 `C` 具有类型参数 `a_1, ..., a_n`。这些类型参数的推断方式与类型化模式 `(_: ´C[a_1, ..., a_n]´) ` 相同。

示例

考虑程序片段

val x: Any
x match {
  case y: List[a] => ...
}

这里,类型模式 `List[a]` 与预期类型 `Any` 匹配。该模式绑定了类型变量 `a`。由于 `List[a]` 对于每个类型参数都符合 `Any`,因此 `a` 没有约束。因此,`a` 被引入为一个没有边界的抽象类型。`a` 的作用域是其 case 子句的右侧。

另一方面,如果x被声明为

val x: List[List[String]],

这会生成约束List[a] <: List[List[String]],简化为a <: List[String],因为List是协变的。因此,a被引入,其上限为List[String]

示例

考虑程序片段

val x: Any
x match {
  case y: List[String] => ...
}

Scala 在运行时不维护有关类型参数的信息,因此无法检查x是否为字符串列表。相反,Scala 编译器将擦除模式为List[_];也就是说,它只会测试值x的顶层运行时类是否符合List,如果符合,则模式匹配将成功。这可能会导致以后出现类转换异常,在列表x包含除字符串以外的元素的情况下。Scala 编译器将使用“未经检查”的警告消息标记这种潜在的类型安全丢失。

示例

考虑程序片段

class Term[A]
class Number(val n: Int) extends Term[Int]
def f[B](t: Term[B]): B = t match {
  case y: Number => y.n
}

模式y: Number的预期类型为Term[B]。类型Number不符合Term[B];因此,上述规则的第 2 种情况适用。这意味着B被视为另一个类型变量,对其进行推断子类型约束。在我们的例子中,适用的约束是Number <: Term[B],这意味着B = Int。因此,在 case 子句中,B被视为具有下限和上限Int的抽象类型。因此,case 子句的右侧y.n(类型为Int)被发现符合方法声明的结果类型Number

模式匹配表达式

  InfixExpr       ::=  InfixExpr MatchClause
  SimpleExpr      ::=  SimpleExpr ‘.’ MatchClause
  Expr            ::=  PostfixExpr ‘match’ ‘{’ CaseClauses ‘}’
  CaseClauses     ::=  CaseClause {CaseClause}
  CaseClause      ::=  ‘case’ Pattern [Guard] ‘=>’ Block
  ExprCaseClause  ::=  ‘case’ Pattern [Guard] ‘=>’ Expr

一个模式匹配表达式

e match { case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ }

由选择器表达式´e´和´n > 0´个 case 组成。每个 case 由一个(可能带守卫的)模式´p_i´和一个块´b_i´组成。每个´p_i´可能由一个守卫if ´e´补充,其中´e´是一个布尔表达式。模式变量在´p_i´中的作用域包括模式的守卫和相应的块´b_i´。

令´T´为选择器表达式´e´的类型,令´a_1, ..., a_m´为所有包含模式匹配表达式的函数的类型参数。对于每个´a_i´,令´L_i´为其下限,´U_i´为其上限。每个模式´p \in {p_1,, ..., p_n}´可以用两种方式进行类型化。首先,尝试使用´T´作为其预期类型对´p´进行类型化。如果失败,则使用修改后的预期类型´T'´对´p´进行类型化,该类型´T'´是通过将´T´中每个类型参数´a_i´替换为undefined得到的。如果第二步也失败,则会产生编译时错误。如果第二步成功,则令´T_p´为模式´p´作为表达式时的类型。然后确定最小边界´L_11, ..., L_m'´和最大边界´U_1', ..., U_m'´,使得对于所有´i´,´L_i <: L_i'´和´U_i' <: U_i´,并且满足以下约束系统

$$ L_1 <: a_1 <: U_1\;\wedge\;...\;\wedge\;L_m <: a_m <: U_m \ \Rightarrow\ T_p <: T $$

如果找不到这样的边界,则会产生编译时错误。如果找到了这样的边界,则以 ´p´ 开头的模式匹配子句将在以下假设下进行类型检查:每个 ´a_i´ 的下界为 ´L_i'´ 而不是 ´L_i´,上界为 ´U_i'´ 而不是 ´U_i´。

每个代码块 ´b_i´ 的预期类型是整个模式匹配表达式的预期类型。如果没有预期类型,则尝试对所有代码块 ´b_i´ 列表进行 协调。模式匹配表达式的类型是所有代码块 ´b_i´ 在协调后的类型 最小上界

当将模式匹配表达式应用于选择器值时,会按顺序尝试模式,直到找到一个与 选择器值 匹配的模式。假设这种情况是 case ´p_i \Rightarrow b_i´。整个表达式的结果是评估 ´b_i´ 的结果,其中 ´p_i´ 的所有模式变量都绑定到选择器值的相应部分。如果找不到匹配的模式,则会抛出 scala.MatchError 异常。

case 中的模式也可以后跟一个保护后缀 if e,其中 ´e´ 是一个布尔表达式。如果 case 中的先前模式匹配,则会评估保护表达式。如果保护表达式评估为 true,则模式匹配成功。如果保护表达式评估为 false,则 case 中的模式被认为不匹配,并且继续搜索匹配的模式。

为了提高效率,模式匹配表达式的评估可能会尝试以与文本顺序不同的顺序尝试模式。这可能会影响通过保护中的副作用进行的评估。但是,保证只有在保护它的模式匹配时才会评估保护表达式。

如果模式匹配的选择器是 sealed联合类型 或它们的组合的实例,则模式匹配的编译可能会发出警告,诊断给定模式集不完整,即在运行时可能引发 MatchError

示例

考虑以下算术项的定义

abstract class Term[T]
case class Lit(x: Int) extends Term[Int]
case class Succ(t: Term[Int]) extends Term[Int]
case class IsZero(t: Term[Int]) extends Term[Boolean]
case class If[T](c: Term[Boolean],
                 t1: Term[T],
                 t2: Term[T]) extends Term[T]

有一些项表示数字字面量、增量、零测试和条件。每个项都带有类型参数,表示它所代表的表达式的类型(IntBoolean)。

可以编写如下类型安全的评估器来评估这些项。

def eval[T](t: Term[T]): T = t match {
  case Lit(n)        => n
  case Succ(u)       => eval(u) + 1
  case IsZero(u)     => eval(u) == 0
  case If(c, u1, u2) => eval(if (eval(c)) u1 else u2)
}

请注意,评估器充分利用了通过模式匹配可以为封闭方法的类型参数获取新边界的这一事实。

例如,第二种情况中模式的类型,Succ(u),是Int。只有当我们假设T 的上限和下限为Int 时,它才符合选择器类型T。在假设Int <: T <: Int 下,我们也可以验证第二种情况的类型右侧,Int 符合其预期类型T

模式匹配匿名函数

  BlockExpr ::= ‘{’ CaseClauses ‘}’

匿名函数可以通过一系列情况来定义

{ case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ }

这些情况作为表达式出现,没有先前的match。这种表达式的预期类型必须部分定义。它必须是scala.Function´k´[´S_1, ..., S_k´, ´R´](对于某些´k > 0´),或者scala.PartialFunction[´S_1´, ´R´],其中参数类型´S_1, ..., S_k´ 必须完全确定,但结果类型´R´ 可能未确定。

如果预期类型是SAM 可转换scala.Function´k´[´S_1, ..., S_k´, ´R´],则表达式被视为等效于匿名函数

(´x_1: S_1, ..., x_k: S_k´) => (´x_1, ..., x_k´) match {
  case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´
}

这里,每个´x_i´都是一个新的名称。如这里所示,这个匿名函数又等效于以下实例创建表达式,其中´T´ 是所有´b_i´ 类型 的最小上界。

new scala.Function´k´[´S_1, ..., S_k´, ´T´] {
  def apply(´x_1: S_1, ..., x_k: S_k´): ´T´ = (´x_1, ..., x_k´) match {
    case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´
  }
}

如果预期类型是scala.PartialFunction[´S´, ´R´],则表达式被视为等效于以下实例创建表达式

new scala.PartialFunction[´S´, ´T´] {
  def apply(´x´: ´S´): ´T´ = x match {
    case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´
  }
  def isDefinedAt(´x´: ´S´): Boolean = {
    case ´p_1´ => true ... case ´p_n´ => true
    case _ => false
  }
}

这里,´x´ 是一个新的名称,´T´ 是所有´b_i´ 类型 的最小上界。如果模式´p_1, ..., p_n´ 中已经有一个变量或通配符模式,则isDefinedAt 方法中的最终默认情况将被省略。

示例

以下是一个使用foldLeft 计算两个向量标量积的示例

def scalarProduct(xs: Array[Double], ys: Array[Double]) =
  (xs zip ys).foldLeft(0.0) {
    case (a, (b, c)) => a + b * c
  }

此代码中的情况子句等效于以下匿名函数

(x, y) => (x, y) match {
  case (a, (b, c)) => a + b * c
}