Haskell 筆記 (四) 函數

haskell 函數

模式

模式匹配

模式匹配就是通過檢查數據的特定結構來檢查是否匹配,並按模式從中解析出數據

定義函數時可以定義多個不同模式,調用時會按照從上到下依次匹配,匹配成功則調用相應的函數體。

萬能模式:模式中給出一個小寫字母的名字,而非具體的值,將會總能匹配輸入,稱爲萬能模式。

注意:

  1. 多個匹配模式都滿足時,只會匹配到第一個滿足的模式。定義模式的順序很重要。
  2. 一點要定義一個萬能模式,防止意外輸入導致崩潰。

例子: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作用域

  1. 函數中的where定義的名字只對本函數可見,不用擔心污染其他函數的命名空間。
  2. 函數中多個模式不共享where,where只對定義它的模式可見。
  3. 如果想多個函數或本函數的其他模式匹配中重複使用名字,可以定義到全局。

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."          
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章