haskell 函數
模式
模式匹配
模式匹配
就是通過檢查數據的特定結構
來檢查是否匹配,並按模式從中解析出數據
。
定義函數時可以定義多個不同模式,調用時會按照從上到下
依次匹配,匹配成功則調用相應的函數體。
萬能模式
:模式中給出一個小寫字母的名字
,而非具體的值,將會總能匹配輸入,稱爲萬能模式。
注意:
- 多個匹配模式都滿足時,只會匹配到第一個滿足的模式。定義模式的順序很重要。
- 一點要定義一個萬能模式,防止意外輸入導致崩潰。
例子:showName.hs
showName :: Int -> String
showName 1 = "Sam" --模式1
showName 2 = "Joe" --模式2
showName 3 = "Tim" --模式3
showName x = "Unknown Name" --萬能模式
*Main> :load showName.hs
[1 of 1] Compiling Main ( showName.hs, interpreted )
Ok, one module loaded.
*Main>
*Main>
*Main> showName 1
"Sam"
*Main> showName 2
"Joe"
*Main> showName 3
"Tim"
*Main> showName 4
"Unknown Name"
模式匹配方式完全可以使用 if 實現,但模式匹配方式更簡潔。
不同的模式可以理解爲不同子處理函數,匹配是從上到下,只有第一個匹配的模式可以被執行。
元祖的模式匹配
元祖的模式匹配是通過對元祖各項賦予名字(first, second, third, …), 即可以通過名字匹配到元祖各項。
對於不關心的元祖項可以通過下劃線“_”
忽略,如(_, _, third, …).
列表與列表推導式的模式匹配
推導式列表模式匹配
Prelude> let xs = [(1,2),(3,4),(5,6), (7,7),(8,9)]
Prelude> [a + b | (a, b) <- xs]
[3,7,11,14,17]
列表的模式匹配
[] 匹配空列表
:[]匹配非空列表, [1, 2, 3] 等價於 1:2:3:[]
x:xs模式
在Haskell應用很廣泛,它將列表頭元素綁定到x, 餘下部分綁定xs。 如果列表只有一個元素,那麼xs將是一個空列表。
as模式
as模式
允許按模式將值分爲多個項,同時保留對整體的引用。格式爲: 名字@模式
定義
firstLetter :: String -> String
firstLetter "" = "Empty"
firstLetter all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x] --(x:xs)分解的參數
執行
firstLetter "Hashell"
"The first letter of Hashell is H"
哨位 Guard
Guard用來檢測參數的值,和其他語言if類似。 Guard跟在“|”的右邊。
Guard是一個布爾表達式的值
。爲True就執行相應的函數體。
注意: 每條Guard至少縮進1個空格,建議縮進4個空格,更容易閱讀
showScore :: Int -> String
showScore score
| score <= 10 = "0 ~ 10 "
| score <= 50 = "11 ~ 50 "
| score <= 100 = "51 ~ 100 "
| otherwise = "score error" --otherwise,捕獲一切未處理的條件
執行
showScore 7
"0 ~ 10 "
*Main> showScore 11
"11 ~ 50 "
*Main> showScore 80
"51 ~ 100 "
*Main> showScore 110
"score error"
和模式比較像,區別在與模式是對參數的結構匹配
和解析值
,Guard是對參數值的檢查
。
where
where 關鍵字用來保存中間結果。 可以減少重複計算
和提升可讀性
。
showScore :: Int -> String
showScore score
| doubleScore <= lowScore = "0 ~ 10 "
| doubleScore <= midScore = "11 ~ 50 "
| doubleScore <= highScore = "51 ~ 100 "
| otherwise = "score error"
where doubleScore = score * 2 --doubleScore保存計算值,前面的Guard都可以使用,減少重複計算
lowScore = 10 --通過對值的命名,提升可讀性
midScore = 50 --通過對值的命名,提升可讀性
highScore = 100 --通過對值的命名,提升可讀性
注意: where 定義的所有變量的起始必須對齊在同一列。
執行
*Main> showScore 3
"0 ~ 10 "
*Main> showScore 6
"11 ~ 50 "
*Main> showScore 50
"51 ~ 100 "
*Main> showScore 51
"score error
另外where中也可以使用模式匹配, 還可以定義函數
showScore :: Int -> String
showScore score
| doubleScore score <= lowScore = "0 ~ 10 "
| doubleScore score <= midScore = "11 ~ 50 "
| doubleScore score <= highScore = "51 ~ 100 "
| otherwise = "score error"
where doubleScore s = score * 2
(lowScore, midScore, highScore) = (10, 50, 100) --通過元祖的模式匹配賦值
where作用域
- 函數中的where定義的名字只對本函數可見,不用擔心污染其他函數的命名空間。
- 函數中多個模式不共享where,where只對定義它的模式可見。
- 如果想多個函數或本函數的其他模式匹配中重複使用名字,可以定義到全局。
let
let表達式和where表達式很相似。
let表達式格式: let <bindings> in <expressions>
let 綁定的名字只在in的部分可見。
let和where區別:
let | where | |
---|---|---|
定義 | 表達式,意味着可以放在代碼任意位置 | 關鍵字,位置固定 |
作用域 | 作用域小,guard內可見 | 作用域大,函數中所有Guard都可見 |
語法 | 先綁定值,後使用表達式 | 先使用表達式,後綁定值 |
可讀性 | 聲明更近一些,可讀性高 | 聲明在函數體底部 |
case 表達式
case表達式格式
case expression of pattern -> result
pattern -> result
pattern -> result
...
case表達式和函數參數的模式匹配十分的相似,函數參數的模式只能在定義函數時使用,case是表達式, 可以在任何地方使用。
case例子
describeList :: [a] -> String
describeList ls = "The list is " ++ case ls of [] -> "empty."
[x] -> "a singleton list."
xs -> "a longer list."
函數定義的模式匹配本質上就是case表到式的語法糖,這樣寫也是等價的:
describeList :: [a] -> String
describeList ls = "The list is " ++ what ls --調用函數what
where what [] = "empty." --where中定義了函數what的模式匹配
what [x] = "a singleton list."
what xs = "a longer list."