pattern matching 這個許多語言都有的概念,它的常見形式會很像一個switch cass,然後根據不一樣的 case/pattern 決定執行不一樣的程式碼片段,而在 Haskell 中我們是在 function 定義時寫入特定的模式來達成這件事情。
舉個例子我想寫一個 function ,如果讀到特定字元就輸出特定字串的話我可以這樣寫:
word 'a' = "Apple"
word 'b' = "Banana"
word 'c' = "Candy"
word 'a' -- "Apple"
word 'b' -- "Banana"
word 'c' -- "Candy"
但如果我們超出我們所規定的 pattern 的話
word 'd'
"*** Exception: <interactive>:116:1-18: Non-exhaustive patterns in function word
所以通常我還是需要定義一個通用的 pattern 這樣才能避免 runtime 的錯誤
那我們也可以用 Pattern matching 來達成之前遞迴加總 List 的元素
sumList :: [Int] -> Int
sumList [] = 0
sumList (x:xs) = x + sumList xs
print (sumList [1..5]) -- 15
我們先定義了sumList [] = 0
意思是 sumList
的參數為 []
時會回傳 0
,而當參數不是 []
時就是會到下一行 sumList (x:xs) = x + sumList xs
我知道也許有人初見這種 expression 會感到疑惑。
首先只要先記得 [1,2,3]
就是 1:[2,3]
(精確來說是 1:2:3:[]
) 的語法糖,那這邊我們就能用 (x:xs)
的形式去拿出 List 中的第一個元素及剩餘的List 。 然後我就把第一項與放入 sumList
的剩餘 List 相加即可。
在 Haskell 中還有兩個蠻常見搭配 pattern matching 的語法 @
及 _
concatFirstElement :: [String] -> String
concatFirstElement [] = "Empty list, whoops!"
concatFirstElement all@(x:_) = "The first element of the list " ++ show all ++ " is " ++ x
print (concatFirstElement []) -- "Empty list, whoops!"
print (concatFirstElement ["foo","bar","baz"]) -- "The first element of the list [\"foo\",\"bar\",\"baz\"] is foo"
我們先從_
開始看,第二行的 pattern 是會去匹配 (x:_)
的格式,那_
的意思是我們不在意或者說不會用到這個數值。那意義就是我們只需要x
而他後面:
所串接的 List,我們並不需要。
而 all@(x:_)
就是記住這個 pattern 的 reference ,也就是說 all
就是 (x:_)
這個 List ,所以我們可以在接下來的 expression 如果需要用到整個List 我們不必一直 x:xs
來作為整個 List 。
今天介紹了 pattern matching 的基本運用,明天將會介紹更多 pattern matching 相關的語法~
今天的程式碼:
https://github.com/toddLiao469469/30days-for-haskell