模式匹配 (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