### Subversion checkout URL

You can clone with HTTPS or Subversion.

Fetching contributors…

Cannot retrieve contributors at this time

file 165 lines (162 sloc) 26.304 kb
 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 `                                                            函數的語法

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)

fst和snd可以從序對中取出元素。三元組(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。

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"

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

Ugh！一點都不好讀！繼續進發，用guard實現我們自己的compare函數：

myCompare :: (Ord a) => a -> a -> Ordering  a `myCompare` b      | a > b     = GT      | a == b    = EQ      | otherwise = LT
ghci> 3 `myCompare` 2  GT

Note：通過反單引號，我們不僅可以以中綴形式呼叫函數，也可以在定義函數的時候使用它。有時這樣會更易讀。

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)     where bmi weight height = weight / height ^ 2

where綁定還可以嵌套。有個已被廣泛接受的理念，就是一個函數應該有幾個輔助函數。而每個輔助函數也可以通過where擁有各自的輔助函數。

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

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

`
Something went wrong with that request. Please try again.