1. 程式人生 > >haskell 遞迴和高階函式

haskell 遞迴和高階函式

遞迴實際上是定義函式以呼叫自身的方式。 Haskell 沒有 while 或 for 迴圈的原因,遞迴是我們的替代方案。

邊界條件: 不遞迴的部分, 在遞迴定義中宣告的一兩個非遞迴的值
maximum' :: (Ord a) => [a] -> a   
maximum' [] = error "maximum of empty list"   
maximum' [x] = x   
maximum' (x:xs) = max x (maximum' xs)

固定模式:先定義一個邊界條件,再定義個函式,讓它從一堆元素中取一個並做點事情後,把餘下的元素重新交給這個函式。

Haskell 中把函式可以作為引數和回傳值傳來傳去,這樣的函式就被稱作高階函式。

applyTwice :: (a -> a) -> a -> a   
applyTwice f x = f (f x)
ghci> applyTwice (+3) 10   
16 
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]   
zipWith' _ [] _ = []   
zipWith' _ _ [] = []   
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
flip' :: (a -> b -> c) -> (b -> a -> c)   
flip' f = g   
    where g x y = f y x

 (->) 是右結合. 用空格的函式呼叫符是左結合的,如 f a b c 與 ((f a) b) c 等價,而 $ 則是右結合的。函式組合(.)是右結合的

map :: (a -> b) -> [a] -> [b]   
map _ [] = []   
map f (x:xs) = f x : map f xs

filter :: (a -> Bool) -> [a] -> [a]   
filter _ [] = []   
filter p (x:xs)    
    | p x       = x : filter p xs   
    | otherwise = filter p xs

lambda 就是匿名函式
ghci> map (\(a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]   
[3,8,9,8,7]

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

elem' :: (Eq a) => a -> [a] -> Bool   
elem' y ys = foldl (\acc x -> if x == y then True else acc) False ys

右摺疊 foldr 的行為與左摺疊相似,只是累加值是從 List 的右邊開始。同樣,左摺疊的二元函式取累加值作首個引數,當前值為第二個引數(即 \acc x -> ...),而右摺疊的二元函式引數的順序正好相反(即 \x acc -> ...)。這倒也正常,畢竟是從右端開始摺疊。
map' :: (a -> b) -> [a] -> [b]   
map' f xs = foldr (\x acc -> f x : acc) [] xs

scanl 和 scanr 與 foldl 和 foldr 相似,只是它們會記錄下累加值的所有狀態到一個 List。也有scanl1 和 scanr1
ghci> scanl (+) 0 [3,5,2,1]   
[0,3,8,10,11]   
ghci> scanr (+) 0 [3,5,2,1]   
[11,8,3,1,0]   

(.) :: (b -> c) -> (a -> b) -> a -> c   
f . g = \x -> f (g x)

($) :: (a -> b) -> a -> b   
f $ x = f x