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."