1.foldr
foldr::(a->b->b)->b->[a]->b
foldr函數(shù)應(yīng)用在list上,它的功能就像是把list折疊起來一樣,通過使用foldr,最后將list合成一個結(jié)果。它的具體過程是,它只接受一個以兩個參數(shù)的函數(shù),第二個參數(shù)作為函數(shù)的第二個輸入,List中的元素均作為函數(shù)的第一個參數(shù)進行運算,在進行第一次運算后返回的結(jié)果作為下一步的函數(shù)的第二個參數(shù)。
例如
Input:?foldr (+) 5 [1,2,3,4]
Output:?15
具體來看即: 4+5=9 => 3+9 =12 => 2+12 = 14 => 1+14=15
復(fù)雜的例子即:
alternative :: [Integer] -> Integer
alternative = foldr (\x y->if x< 20 then 5*x-3 + y else y) 0
alternative [1,2,3] =21
具體來看即: 3*5-3+0=12=>2*5-3+12=19=>1*5-3+19=21
2.函數(shù)參數(shù),Maybe Type recursion
通常情況下,我們定義一個以List為參數(shù)的函數(shù),即:
f::[Int]->[Int]
f [] = ..
f (x:xs) = ..
單數(shù)如果出于某些原因,我們希望每次提取兩個元素作為參數(shù),那么我們可以具體定義,例如,我們有encode函數(shù)和decode函數(shù):
encode :: [Color] -> [Bit]
? ? ? ? encode []? ? ? ? ? = []
? ? ? ? encode (Red:xs)? ? = O:O:(encode xs)
? ? ? ? encode (Blue:xs)? = O:I:(encode xs)
? ? ? ? encode (Yellow:xs) = I:O:(encode xs)
Color類型中有三個元素,為了節(jié)約Bit起見,我們用兩個Bit表達。那么在decode函數(shù)中,每次必須提取兩個元素,其它不符合條件的皆為Nothing。我們有多種方法,此處介紹兩種:
方案一:
decode :: [Bit] -> Maybe [Color]
decode []= Just []
decode (O:O:xs) =
? case decode xs of
? ? Nothing -> Nothing
? ? Just ys -> Just (Red:ys)
decode (O:I:xs) =
? case decode xs of
? ? Nothing -> Nothing
? ? Just ys -> Just (Blue:ys)
decode (I:O:xs) =
? case decode xs of
? ? Nothing -> Nothing
? ? Just ys -> Just (Yellow:ys)
decode _ = Nothing
列舉所有可以decode為顏色的輸入,然后將其decode為Color然后再翻譯之后的list, 翻譯后會得到一個color類型的list或者Nothing。 這里有一個問題是這個函數(shù)看起來不是那么的完整(non-exhaustive),因為我們沒有列舉[I,I]的情況。為了更好的說明這個函數(shù)的結(jié)構(gòu),我寫出一個相對簡單的decode函數(shù):
data Bit = O | I
? deriving (Eq, Show, Enum, Bounded)
decode::[Bit]->Maybe [Bool]
decode [] = Just []
decode (I:xs) = case decode xs of
? ? ? ? ? ? Nothing -> Nothing
? ? ? ? ? ? Just bs -> Just (True:bs)
decode _ = Nothing
main::IO()
main = print (decode [I,I,I])
注意函數(shù)最后一個定義,它的意思是不管什么輸入,它都將返回Nothing。
此處涉及到Haskell函數(shù)的pattern matching。 Haskell在調(diào)用函數(shù)時,會應(yīng)用第一個可以調(diào)用的條件。 譬如在這個函數(shù)中, 如果我們 decode [],它會返回 Just [], 而不是 Nothing。decode的核心在 decode (I:xs)這一定義中。如果輸入為I打頭的list,那函數(shù)會對去掉I的list再進行decode,它會返回一個list,或者Nothing。如果是Nothing,那么原始的調(diào)用將返回Nothing,若為一個list,那么它會將這個list的append在第一個decode的結(jié)果之后即True:bs。
我們考慮decode [I,I,I]。?
decode [I,I,I] => decode [I,I]=> decode [I] => decode []
此處代表 [I,I,I] 取決于 [I,I]的結(jié)果, [I,I] 取決于 [I],[I] 取決于 []。
而又因為: Just [] -> Just True:[] -> Just True:True:[]
所以最終結(jié)果為: Just (True:bs) = Just (True:True:True:[]) = Just [True,True,True]
而如果是 decode [I,O,I] 的話:
decode [I,O,I] -> decode [O,I] == Nothing
這里在第一步我們有類型 1:xs, 所以會進入第二個case即對后面的內(nèi)容進行decode。而后我們有[O,I],此時將從函數(shù)第一條開始對應(yīng)O:xs類型不是[],也不是I:xs,那么就會進入最后一個定義,decode _ = Nothing。那么此時將直接進入Nothing case,返回了Nothing,原始函數(shù)取得了bs為Nothing的消息后,進入了Nothing case返回了Nothing。此類定義有一個好處就是,在第一次看到O的時候,就會終止繼續(xù)迭代。
回到我們的color decode的函數(shù)中來。通過上述例子我們可以清晰的了解這個函數(shù)的結(jié)構(gòu),它列舉了所有可decode的情況,然后用相同的方法進行迭代,在第一次無法進行decode的情況下就返回Nothing,終止運算。
那么我們可不可以把可decode的情況全部分開來寫,然后直接寫一個decode function去綜合這幾種情況呢?當然可以!
decode :: [Bit] -> Maybe [Color]
decode [] = Just []
decode [O,O] = Just [Red]
decode [O,I] = Just [Blue]
decode [I,O] = Just [Yellow]
decode (a:b:xs) = case decode [a,b] of
? ? ? ? ? ? ? ? Nothing->Nothing
? ? ? ? ? ? ? ? Just c -> case decode xs of
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Nothing -> Nothing
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Just cs -> Just (c++cs)
decode _ = Nothing
我們可以首先把幾種decode的方法寫出來,最后用a:b來泛指這三種情況。但是此處我們要先判斷[a,b]是否可被decode即會不會有[I,I]的情況,然后再看剩余l(xiāng)ist的decode情況--進行迭代。比較神奇的是這種方式運行的結(jié)果會比上一種慢一些..
方案二:
decode :: [Bit] -> Maybe [Color]
decode []= Just []
decode (O:O:xs) = add Red (decode xs)
decode (O:I:xs) = add Blue (decode xs)
decode (I:O:xs) = add Yellow (decode xs)
decode _ = Nothing
add :: Color -> Maybe [Color] -> Maybe [Color]
add color xs =
? case xs of
? ? Nothing -> Nothing
? ? Just ys -> Just (color:ys)
這種方案更容易理解。我們需要一個輔助函數(shù)add,來把Color 和 Just [Color] 類型組成list,像“:” operator一樣。 由于此時是Maybe Type,我們無法應(yīng)用“:”operator,為此構(gòu)建了add。剩余的則為基本迭代,decode一對然后再一對,每次的結(jié)果都返回成list。