语法
语法(模式匹配,哨卫)
|几天不写Blog了,先写个Haskell凑个数。估计再写两次就赶上现在学的进度了。
参考书籍: Learn you a haskell
模式匹配
定义
模式匹配(pattern matching)通过检查数据的特殊结构来检查是否匹配,并按模式从中解析出数据。
在Haskell中,函数经常要用到模式匹配,并且可以匹配一切数据类型。譬如,我们有一个检查传进的数是否是7的函数: 1
2
3lucky :: Int -> String
lucky 7 = "LUCKY NUMBER SEVEN!"
lucky x = "Sorry, you're out of luck, pal!"lucky()时,函数会将传入的参数从上到下进行匹配,一旦有匹配,对应的函数体就会被调用。其功能其实类似于if-else语句。
如果我们在模式中给出一个小写字母,那这就是一个万能模式(catchall pattern)。他总能匹配输入的参数,并可以在后面调用。
这里还有一个斐波那契数列的例子: 1
2
3factorial :: Int -> String
factorial 0 = 1
factorial n = n * factorial (n - 1)
这里,我们使用了递归来实现,并通过模式匹配来处理了边界值。后面我们会知道,这在Haskell中是十分常见的一种用法。
另外,模式匹配的最后一定要加入一个万能模式,即该函数必须能够处理所有情况,否则调用时用非常规值会发生错误。
元组的模式匹配
元组同样可以使用模式匹配。譬如下面的计算二维向量和的函数: 1
2
3
4
5
6--不对元组内部进行模式匹配
addVectors :: (Double, Double) -> (Double, Double) -> (Double, Double)
addVectors a b = (fst a + fst b, snd a + snd b)
--对元组进行模式匹配
addVectors :: (Double, Double) -> (Double, Double) -> (Double, Double)
addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
占位符
在模式匹配时我们有可能只需要考虑其中的某些值,对于其他值不予考虑。这种情况一般发生在使用元组时。比如我们想要看元组的第一个数是否为7: 1
2
3
4
5
6
7
8--我们固然可以使用万能匹配,如这样
lucky :: (Int, Int) -> String
lucky (7, y) = "Lucky for you!"
lcuky (x, y) = "You are not lucky!"
--但是我们也可以用占位符,像这样
lucky :: (Int, Int) -> String
lucky (7, _) = "Lucky for you!"
lucky (_, _) = "You are not lucky!"1
2first :: (a,b,c) -> x
first (x, _, _) = x
列表和列表推导式的模式匹配
在列表推导式中也可以使用模式匹配: 1
2
3ghci> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]
ghci> [a+b | (a,b) <- xs]
[4, 7, 6, 8, 11, 4][]来匹配空列表,也可以配合:来匹配非空列表(在Haskell中[1, 2, 3]就是1:2:3:[]的语法糖)。比如我们可以写一个head函数: 1
2
3head :: [a] -> a
head [] = error "Can't call head on an empty list, dummy!"
head (x:_) = x1
2badAdd :: (Num a) => [a] -> a
badAdd (x:y:z:[]) = x + y + z
As模式
as模式(as-pattern)允许我们按模式把一个值分割成多个项,同时保留对整体的引用。要使用as模式,只要将一个名字和@置于普通模式的前面即可。
比如我们有一个取出第一个字符的函数定义如下: 1
2
3firstletter :: String -> String
firstletter "" = ”Empty string, whoops!"
firstletter all@(x:xs) = "The first letter of " ++ all ++ "is " ++ [x]1
2ghci> firstletter "Songer"
"The first letter of Songer is S"
哨卫
模式用来检查参数的结构是否匹配,哨卫(guard)则用来检查参数的性质是否为真。其亦与if语句相似,但是可读性更高。
我们这里先看一个用到哨卫的函数,根据你的分数按照A,B,C,D进行打分: 1
2
3
4
5
6
7judge :: Int -> String
judge score
true| score<60 = "You got a D, fool!"
| score<75 = "You got a C!"
| score<85 = "You got a B,not bad!"
| score<=100 = "Congratulation, you got an A!"
| otherwise = "Error score!"
哨卫跟在竖线(|)的右边,一个哨卫就是一个布尔表达式,如果计算为True,则选择对应的函数体,否则计算下一个函数体。每条哨卫语句至少缩进一个空格。
与模式匹配一样,在最后一般要加上一个万能的情况以提升鲁棒性。