8種Scala模式匹配技巧

1.列表提取器

列表可以通過多種功能強大的模式匹配來解構。首先是要構建清單。

<code>val countingList = List(1,2,3,42)/<code>

使用類似於案例類構造函數的模式從此列表中提取任何元素:

<code>val mustHaveThree = countingList match {
case List(_, _, 3, somethingElse) => s“ 有一個包含3作為第三個元素的列表,之後我發現$ somethingElse”
}/<code>

此模式將一個列表與四個元素完全匹配,其中我們不關心前兩個元素。第三個必須恰好是3就行了,第四個可以是任何東西,之所以將其命名為somethingElse,是因為可以在s插值字符串中重用它。

2. Haskell-Like前置

如果認為清單與以前相同,則可以按照下面的提取清單的開頭和結尾:

<code>val startsWithOne = countingList match {
case 1 :: someOtherElements => “此列表以一個開頭,其餘為$ someOtherElements”
}/<code>

不要問這怎麼可能。即將就是主題了。前置模式在處理列表的代碼中非常有用,別忘記啦,但是當事先不知道列表是否為空時,可以這樣寫:

<code>def processList(numbers: List[Int]): String = numbers match {
case Nil => ""
case h :: t => h + " " + processList(t)
}/<code>

熟悉Haskell的人可能非常熟悉這種處理列表的方式。

8種Scala模式匹配技巧

3.列出Vararg模式

上面顯示的第一個模式只能將列表限制為一定數量的元素。如果不知道元素的數量又咋辦?

<code>val dontCareAboutTheRest = countingList match {
case List(_, 2, _*) => "只關心此列表具有2,作為第二個元素"
}/<code>

_ _*是重要的位,表示“任何數量的附加參數”。這種模式更加靈活,幾乎無數個列表可以匹配該模式,而不是我們之前使用的4元素列表模式。唯一_*要注意的是它必須是模式中的最後一位。換句話說,該情況下,List(_, 2, _*, 55),將無法被翻譯。

4.其他列表中綴模式

當我們可以測試列表的開頭,甚至測試列表中的元素時,它很有用。但我們要測試列表的最後一個元素呢?

<code>val mustEndWithMeaningOfLife = countingList match {
case List(1,2,_) :+ 42 =>
}/<code>

:+是追加操作符,這很像::從視圖模式匹配的點。也可以使用+:prepend運算符,但我更喜歡::

<code>val mustEndWithMeaningOfLife2 = countingList match {
case List(1, _*) :+ 42 =>
}/<code>

5.類型說明符

有時,實際上並不關心要匹配的值,而只關心它們的類型。

<code>def gimmeAValue(): Any = { ... }
val gimmeTheType = gimmeAValue() match {
case _: String =>
case _: Int =>
case _ =>
}/<code>

該:String位是重要的組成部分。它允許案例僅匹配那些符合該類型的模式。當捕獲異常時,特別有用。

<code>try { 
...
} catch {
case _: IOException =>
case _: Exception =>
case _: RuntimeException =>
}/<code>

類型防護的缺點是它們基於反射。別忘記了,這點真的容易犯錯。

6.名稱綁定

我看過以下模式的次數超出了我的預期:

<code>def requestMoreInfo(p: Person): String = { ... }
val bob = Person("Bob", 34, List(“ Inception”,“ The Departed”))
val bobsInfo = bob match {
case Person(name, age, movies) => s“ $ name的信息:$ {requestMoreInfo(Person(姓名,年齡,電影))}”

}/<code>

我們解構一個案例類只是為了用相同的數據重新實例化它,以便以後使用。如果不關心case類中的任何字段,但是,如果關心的不是全部和整個實例,又該咋辦呢?

<code>val bobsInfo = bob match {
case p @ Person(name, _, _) => s“ $ name的信息:$ {requestMoreInfo(p)}”
}/<code>

答:命名要匹配的模式,以便以後可以重用。甚至可以命名子模式:

<code>val bobsInception = xiaoming match {
case Person(name, _, movies @ List("Inception", _*)) => s“ $ name真的很喜歡Inception,其他電影也很喜歡:$ movies”
}/<code>

7. Conditional Guards

如果像我一樣,可能至少嘗試過一次模式匹配滿足條件的內容,由於只知道“任何”和“恆定”模式,因此放棄了模式匹配,而是使用了鏈式if-elses,這也是經常的。

<code>val ordinal = gimmeANumber() match {
case 1 => "first"
case 2 => "second"
case 3 => "third"
case n if n % 10 == 1 => n + "st"
case n if n % 10 == 2 => n + "nd"
case n if n % 10 == 3 => n + "rd"
case n => n + "th"
}/<code>

如上所示,if防護直接位於模式中。另請注意,該條件沒有括號。

8.替代模式

如果針對多個模式返回相同的表達式,則無需c+v去複製相同的代碼。

<code>val myOptimalList = numbers match {
case List(1, _, _) => “我喜歡這個列表”
case List(43, _*) => “我喜歡這個列表”
case _ => “我不喜歡這個列表”
}/<code>

也可以將返回相同表達式的模式組合為一個模式:

<code>val myOptimalList = numbers match {
case List(1, _, _) | List (43, _*) => “我喜歡這個列表”
case _ => “我不喜歡這個列表”
}/<code>

這種模式的唯一缺點是不能綁定任何名稱,因為無法確保這些值在右側可用。

在許多情況下,例如,想處理多種異常時,此模式在實踐中很有用:

<code>try {
...
} catch {
case _: RuntimeException | _: IOException => ""
}/<code>

最後,沒什麼好說的,大家今天都過得愉快吧。


分享到:


相關文章: