無選項樣式比對
與 Scala 2 相比,Scala 3 中模式比對的實作大幅簡化。從使用者的角度來看,這表示 Scala 3 產生的模式會容易除錯許多,因為所有變數都會顯示在除錯模式中,且位置也正確保留。
Scala 3 支援 Scala 2 萃取器 的超集。
萃取器
萃取器是公開方法 unapply
或 unapplySeq
的物件
def unapply(x: T): U
def unapplySeq(x: T): U
其中 T
是任意型別,如果它是受檢物件型別 Scrut
的子型別,則會在呼叫方法前執行型別測試。U
遵循 固定元數萃取器 和 可變元數萃取器 中所述的規則。
注意:U
可以是萃取器物件的類型。
unapply
和 unapplySeq
實際上可以有更通用的簽章,允許領先類型子句,以及任意多個使用子句,在常規術語子句之前和之後,以及最多一個隱含子句在結尾,例如
def unapply[A, B](using C)(using D)(x: T)(using E)(using F)(implicit y: G): U = ???
公開方法 unapply
的萃取器稱為固定元數萃取器,適用於固定元數模式。公開方法 unapplySeq
的萃取器稱為可變元數萃取器,適用於可變元數模式。
固定元數萃取器
固定元數萃取器公開下列簽章(帶有潛在類型、使用和隱含子句)
def unapply(x: T): U
類型 U
符合下列其中一項比對
或 U
符合類型 R
type R = {
def isEmpty: Boolean
def get: S
}
且 S
符合下列其中一項比對
unapply
的前一種形式優先權較高,而單一比對優先權高於基於名稱的比對。
注意:R
中的 S
可以是 U
。
如果符合下列其中一項條件,固定元數萃取器的使用是不可反駁的
U = true
- 萃取器用作乘積比對
U <: R
且U <: { def isEmpty: false }
U = Some[T]
注意:由於相容性原因,最後一項規則是必要的,因為 Some
上的 isEmpty
回傳類型為 Boolean
而不是 false
,即使它總是回傳 false
。
布林比對
U =:= Boolean
- 在完全
0
個樣式上進行模式比對
例如
object Even:
def unapply(s: String): Boolean = s.size % 2 == 0
"even" match
case s @ Even() => println(s"$s has an even number of characters")
case s => println(s"$s has an odd number of characters")
// even has an even number of characters
產品比對
U <: 產品
N > 0
是U
中連續 (val
或無參數def
)_1: P1
..._N: PN
成員的最大數目- 在類型為
P1, P2, ..., PN
的完全N
個樣式上進行模式比對
例如
class FirstChars(s: String) extends Product:
def _1 = s.charAt(0)
def _2 = s.charAt(1)
// Not used by pattern matching: Product is only used as a marker trait.
def canEqual(that: Any): Boolean = ???
def productArity: Int = ???
def productElement(n: Int): Any = ???
object FirstChars:
def unapply(s: String): FirstChars = new FirstChars(s)
"Hi!" match
case FirstChars(char1, char2) =>
println(s"First: $char1; Second: $char2")
// First: H; Second: i
單一比對
- 在類型為
S
的1
個樣式上進行模式比對
例如,其中 Nat <: R
,S = Int
class Nat(val x: Int):
def get: Int = x
def isEmpty = x < 0
object Nat:
def unapply(x: Int): Nat = new Nat(x)
5 match
case Nat(n) => println(s"$n is a natural number")
case _ => ()
// 5 is a natural number
基於名稱的比對
S
有N > 1
個成員,使它們每個都是val
或無參數def
,並從類型為P1
的_1
命名到類型為PN
的_N
S
沒有滿足前一點的N+1
個成員,即N
為最大值- 在類型為
P1, P2, ..., PN
的完全N
個樣式上進行模式比對
例如,其中 U = AlwaysEmpty.type <: R
,S = NameBased
object MyPatternMatcher:
def unapply(s: String) = AlwaysEmpty
object AlwaysEmpty:
def isEmpty = true
def get = NameBased
object NameBased:
def _1: Int = ???
def _2: String = ???
"" match
case MyPatternMatcher(_, _) => ???
case _ => ()
可變參數萃取器
可變參數萃取器公開以下簽章 (使用潛在類型、using 和隱含子句)
def unapplySeq(x: T): U
其中 U
必須滿足以下條件
- 設定
V := U
- 如果
V
符合以下比對之一,則V
有效
- 否則
U
必須符合類型R
type R = {
def isEmpty: Boolean
def get: S
}
- 設定
V := S
,並重新嘗試 2,如果失敗,則U
無效。
unapplySeq
的 V := U
形式具有較高優先順序,而序列比對相對於產品序列比對具有較高優先順序。
注意:這表示如果 V := U
形式有效,則會忽略 isEmpty
如果符合以下條件之一,則可變參數萃取器的使用是不可否認的
- 萃取器直接用作序列比對或產品序列比對
U <: R
且U <: { def isEmpty: false }
U = Some[T]
注意:由於相容性原因,最後一項規則是必要的,因為 Some
上的 isEmpty
回傳類型為 Boolean
而不是 false
,即使它總是回傳 false
。
注意:小心,根據第一個條件和上面的說明,可以用 def isEmpty: true
定義一個不可否認的萃取器。這強烈不建議,如果在野外發現,幾乎可以肯定是一個錯誤。
序列比對
V <: X
type X = {
def lengthCompare(len: Int): Int // or, `def length: Int`
def apply(i: Int): T1
def drop(n: Int): scala.Seq[T2]
def toSeq: scala.Seq[T3]
}
T2
和T3
符合T1
- 模式比對完全
N
個簡單模式,類型為T1, T1, ..., T1
,其中N
為序列的執行時期大小,或 - 模式比對
>= N
個簡單模式和一個可變參數模式 (例如,xs: _*
),類型為T1, T1, ..., T1, Seq[T1]
,其中N
為序列的最小大小。
例如,其中 V = S
,U = Option[S] <: R
,S = Seq[Char]
object CharList:
def unapplySeq(s: String): Option[Seq[Char]] = Some(s.toList)
"example" match
case CharList(c1, c2, c3, c4, _, _, _) =>
println(s"$c1,$c2,$c3,$c4")
case _ =>
println("Expected *exactly* 7 characters!")
// e,x,a,m
乘積序列比對
V <: Product
N > 0
是V
中連續 (val
或不帶參數的def
)_1: P1
..._N: PN
成員的最大數量PN
符合 Seq Pattern 中定義的簽章X
- 模式比對完全
>= N
個模式,前N - 1
個模式的類型為P1, P2, ... P(N-1)
,其餘模式的類型會如 Seq Pattern 中所述決定。
例如,其中 V = S
,U = Option[S] <: R
,S = (String, PN) <: Product
,PN = Seq[Int]
class Foo(val name: String, val children: Int*)
object Foo:
def unapplySeq(f: Foo): Option[(String, Seq[Int])] =
Some((f.name, f.children))
def foo(f: Foo) = f match
case Foo(name, x, y, ns*) => ">= two children."
case Foo(name, ns*) => "< two children."
有進一步簡化的計畫,特別是將乘積比對和基於名稱的比對納入單一類型的萃取器中。
類型測試
使用 ClassTag
的抽象類型測試已改為 TypeTest
或別名 Typeable
。
- 抽象類型的模式
_: X
需要範圍內的TypeTest
- 使用抽象類型的 unapply 的模式
x @ X()
需要範圍內的TypeTest