# 函数的语法

## 模式匹配 (Pattern matching)

lucky :: (Integral a) => a -> String
lucky 7 = "LUCKY NUMBER SEVEN!"
lucky x = "Sorry, you're out of luck, pal!"   

sayMe :: (Integral a) => a -> String
sayMe 1 = "One!"
sayMe 2 = "Two!"
sayMe 3 = "Three!"
sayMe 4 = "Four!"
sayMe 5 = "Five!"
sayMe x = "Not between 1 and 5"  

factorial :: (Integral a) => a -> a
factorial 0 = 1
factorial n = n * factorial (n - 1)  

charName :: Char -> String
charName 'a' = "Albert"
charName 'b' = "Broseph"
charName 'c' = "Cecil"  

ghci> charName 'a'
"Albert"
ghci> charName 'b'
"Broseph"
ghci> charName 'h'
"*** Exception: tut.hs:(53,0)-(55,21): Non-exhaustive patterns in function charName  

addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
addVectors a b = (fst a + fst b, snd a + snd b)  

addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)  

there we go！好多了！注意，它已经是个万能的匹配了。两个 addVector 的型别都是 addVectors:: (Num a) => (a,a) -> (a,a) -> (a,a)，我们就能够保证，两个参数都是序对 (Pair) 了。

fstsnd 可以从序对中取出元素。三元组 (Tripple) 呢？嗯，没现成的函数，得自己动手：

first :: (a, b, c) -> a
first (x, _, _) = x

second :: (a, b, c) -> b
second (_, y, _) = y

third :: (a, b, c) -> c
third (_, _, z) = z  

ghci> 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]   

*Note*：x:xs 这模式的应用非常广泛，尤其是递回函数。不过它只能匹配长度大于等于 1 的 List。

head' :: [a] -> a
head' [] = error "Can't call head on an empty list, dummy!"
head' (x:_) = x  

ghci> head' [4,5,6]
4
'H'  

tell :: (Show a) => [a] -> String
tell [] = "The list is empty"
tell (x:[]) = "The list has one element: " ++ show x
tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y
tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y  

length' :: (Num b) => [a] -> b
length' [] = 0
length' (_:xs) = 1 + length' xs  

sum' :: (Num a) => [a] -> a
sum' [] = 0
sum' (x:xs) = x + sum' xs  

capital :: String -> String
capital "" = "Empty string, whoops!"
capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]  
ghci> capital "Dracula"
"The first letter of Dracula is D"  

## 什么是 Guards

bmiTell :: (RealFloat a) => a -> String
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!"  

guard 由跟在函数名及参数后面的竖线标志，通常他们都是靠右一个缩进排成一列。一个 guard 就是一个布尔表达式，如果为真，就使用其对应的函数体。如果为假，就送去见下一个 guard，如之继续。如果我们用 24.3 呼叫这个函数，它就会先检查它是否小于等于 18.5，显然不是，于是见下一个 guard。24.3 小于 25.0，因此通过了第二个 guard 的检查，就返回第二个字串。

bmiTell :: (RealFloat a) => a -> a -> String
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                 = "You're a whale, congratulations!"    

ghci> bmiTell 85 1.90
"You're supposedly normal. Pffft, I bet you're ugly!"  

max' :: (Ord a) => a -> a -> a
max' a b
| a > b     = a
| otherwise = b  

guard 也可以塞在一行里面。但这样会丧失可读性，因此是不被鼓励的。即使是较短的函数也是如此，不过出于展示，我们可以这样重写 max'

max' :: (Ord a) => a -> a -> a
max' a b | a > b = a | otherwise = b  

myCompare :: (Ord a) => a -> a -> Ordering
a myCompare b
| a > b     = GT
| a == b    = EQ
| otherwise = LT  
ghci> 3 myCompare 2
GT  
*Note*：通过反单引号，我们不仅可以以中缀形式呼叫函数，也可以在定义函数的时候使用它。有时这样会更易读。

## 关键字 Where

bmiTell :: (RealFloat a) => a -> a -> String
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                   = "You're a whale, congratulations!"  

bmiTell :: (RealFloat a) => a -> a -> String
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!"
where bmi = weight / height ^ 2  

bmiTell :: (RealFloat a) => a -> a -> String
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)  

initials :: String -> String -> String
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
where (f:_) = firstname
(l:_) = lastname  

where 绑定可以定义名字，也可以定义函数。保持健康的程式语言风格，我们搞个计算一组 bmi 的函数：

calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi w h | (w, h) <- xs]
where bmi weight height = weight / height ^ 2  

where 绑定还可以一层套一层地来使用。 有个常见的写法是，在定义一个函数的时候也写几个辅助函数摆在 where 绑定中。 而每个辅助函数也可以透过 where 拥有各自的辅助函数。

## 关键字 Let

let 绑定与 where 绑定很相似。where 绑定是在函数底部定义名字，对包括所有 guard 在内的整个函数可见。let 绑定则是个表达式，允许你在任何位置定义局部变数，而对不同的 guard 不可见。正如 Haskell 中所有赋值结构一样，let 绑定也可以使用模式匹配。看下它的实际应用！这是个依据半径和高度求圆柱体表面积的函数：

cylinder :: (RealFloat a) => a -> a -> a
cylinder r h =
let sideArea = 2 * pi * r * h
topArea = pi * r ^2
in  sideArea + 2 * topArea  

let 的格式为 let [bindings] in [expressions]。在 let 中绑定的名字仅对 in 部分可见。let 里面定义的名字也得对齐到一列。不难看出，这用 where 绑定也可以做到。那么它俩有什么区别呢？看起来无非就是，let 把绑定放在语句前面而 where 放在后面嘛。

ghci> [if 5 > 3 then "Woo" else "Boo", if 'a' > 'b' then "Foo" else "Bar"]
["Woo", "Bar"]
ghci> 4 * (if 10 > 5 then 10 else 0) + 2
42

let 绑定也可以实现：

ghci> 4 * (let a = 9 in a + 1) + 2
42  

let 也可以定义局部函数：

ghci> [let square x = x * x in (square 5, square 3, square 2)]
[(25,9,4)]  

ghci> (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar)
(6000000,"Hey there!")  

ghci> (let (a,b,c) = (1,2,3) in a+b+c) * 100
600  

calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2]  

List Comprehension 中 let 绑定的样子和限制条件差不多，只不过它做的不是过滤，而是绑定名字。let 中绑定的名字在输出函数及限制条件中都可见。这一来我们就可以让我们的函数只返回胖子的 bmi 值：

calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0] 

(w, h) <- xs 这里无法使用 bmi 这名字，因为它在 let 绑定的前面。

ghci> let zoot x y z = x * y + z
ghci> zoot 3 9 2
29
ghci> let boot x y z = x * y + z in boot 3 4 2
14
ghci> boot
< interactive>:1:0: Not in scope: boot'  

## Case expressions

Haskell 取了这一概念融合其中。如其名，case 表达式就是，嗯，一种表达式。跟 if..elselet 一样的表达式。用它可以对变数的不同情况分别求值，还可以使用模式匹配。Hmm，取一个变数，对它模式匹配，执行对应的程式码块。好像在哪儿听过？啊，就是函数定义时参数的模式匹配！好吧，模式匹配本质上不过就是 case 语句的语法糖而已。这两段程式码就是完全等价的：

head' :: [a] -> a
head' [] = error "No head for empty lists!"
head' (x:_) = x  
head' :: [a] -> a
head' xs = case xs of [] -> error "No head for empty lists!"
(x:_) -> x  

case expression of pattern -> result
pattern -> result
pattern -> result
...  

expression 匹配合适的模式。 一如预期地，第一个模式若匹配，就执行第一个区块的程式码；否则就接下去比对下一个模式。如果到最后依然没有匹配的模式，就会产生运行时错误。

describeList :: [a] -> String
describeList xs = "The list is " ++ case xs of [] -> "empty."
[x] -> "a singleton list."
xs -> "a longer list."  

describeList :: [a] -> String
describeList xs = "The list is " ++ what xs
where what [] = "empty."
what [x] = "a singleton list."
what xs = "a longer list."  `