模式匹配 (Pattern matching)
haskell可以定義同名函數,然後會根據聲明順序搜索與傳入參數最接近的函數簽名,然後調用那個函數,可以看成是強化版的函數重載。模式匹配的參數可以是字面量
--調用名爲lucky的函數時,匹配情況爲
--匹配傳入參數是7的情況
lucky 7 = "LUCKY NUMBER SEVEN!"
--匹配任意的單個傳入參數
lucky x = "Sorry, you're out of luck, pal!"
--如果聲明順序翻轉,則lucky x會先於捕獲所有的參數,和其他語言中(c/c++ java)搜索最接近的函數簽名不同。
再看一個例子
sayMe 1 = "One!"
sayMe 2 = "Two!"
sayMe 3 = "Three!"
sayMe 4 = "Four!"
sayMe 5 = "Five!"
sayMe x = "Not between 1 and 5"
根據此特性實現的遞歸階乘
factorial 0 = 1
factorial n = n * factorial (n - 1)
除了能夠聲明字面量外,函數的參數還可以是列表,元組的一部分
--捕獲了一個列表的三個值,然後直接將他們相加(要實現同樣的行爲在java中需要在方法內取出三個值並聲明局部變量)
--_表示捕獲剩餘的變量並拋棄
add (x:y:z:_) = x +y+z
實現一個遞歸的求數組長度
--遞歸觸底返回
length' [] = 0
--_匹配開頭,xs是一個關鍵字,匹配所有的剩餘元素,和_不同的是他可以引用,而_直接拋棄捕獲到的元素,有點像正則的(?:)
--遞歸將length看成1+剩餘部分的長度
length' (_:xs) = 1 + length' xs
說白了,這個東西就是將一些用到的局部變量用語法糖聲明在參數裏了,可以少寫不少代碼,不過問題就是有時候比較複雜的函數聲明有點難看懂。
Guards
模式匹配只能匹配精確值例如”a”,”b”,”c”,以及列表的部分例如(x:y:_),Guards則是匹配參數的域,有點像常規的switch語句。
--當其他語言的switch來看一下就理解了
bmiTell bmi
| bmi <= 18.5 = "You're underweight, you emo, you!"
| bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
| bmi <= 30.0 = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
bmiTell weight height
| weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!"
| weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
| weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!"
--otherwise就相當於其他語言switch裏的default
| otherwise = "You're a whale, congratulations!"
--聲明成中綴函數的形式
a `myCompare` b
| a > b = GT
| a == b = EQ
| otherwise = LT
haskell的Guards將一個函數分成了兩部分,一部分是匹配結果值,還有一部分是where部分用於聲明局部變量。想想我們往常在命令式語言裏寫的函數,也是先聲明局部變量,然後處理,最後判斷返回值。
唯一的不同就是haskell是將局部變量的聲明後置,跟在where的後面
bmiTell weight height
| bmi <= 18.5 = "You're underweight, you emo, you!"
| bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
| bmi <= 30.0 = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
--bmi就聲明瞭一個局部變量
where bmi = weight / height ^ 2
bmiTell weight height
| bmi <= skinny = "You're underweight, you emo, you!"
| bmi <= normal = "You're supposedly normal. Pffft, I bet you're ugly!"
| bmi <= fat = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
--聲明多個局部變量
where bmi = weight / height ^ 2
skinny = 18.5
normal = 25.0
fat = 30.0
where 綁定也可以使用模式匹配,上面的where部分還可以寫成
where bmi = weight / height ^ 2
(skinny, normal, fat) = (18.5, 25.0, 30.0)
在看一個函數的時候,應該先看where部分,再看匹配部分
where部分還可以定義函數
--在列表內部調用Bmi函數
calcBmis xs = [bmi w h | (w, h) <- xs]
--定義bmi函數,此函數只在外部函數xs內可見
where bmi weight height = weight / height ^ 2
calc xs=[bmi w h|(w,h)<-xs]
where
bmi w h=w/h^2
let 綁定與 where 綁定很相似,不同的是let是前置聲明
cylinder r h =
let sideArea = 2 * pi * r * h
topArea = pi * r ^2
in sideArea + 2 * topArea
歡迎關注我的github https://github.com/luckyCatMiao