《Haskell趣學指南》筆記之高階函數

系列文章數組


一個函數知足如下任一條件即爲高階函數app

  1. 接受函數做爲參數
  2. 將函數做爲返回值

《計算的本質》講得更好一些,我感受這本書這裏講得不通透。ide

柯里化

讓函數只接受一個參數就夠了。 若是想接受兩個參數,就先接受一個,返回一個接受另外一個參數的函數便可。函數

multThree :: Int -> Int -> Int -> Int -- 等價於 Int -> (Int -> (Int -> Int))
multThree x y z = x * y * z 
複製代碼

截斷(section)

divideByTen :: (Floating a) => a -> a 
divideByTen = (/10) -- 這就是截斷
isUpperAlphanum :: Char -> Bool
isUpperAlphanum = (`elem` ['A'..' Z']) -- 這就是截斷
複製代碼

一個須要注意的問題是 (-4) 不能表示截斷,必須換成 (`subtract` 4)工具

函數做參數

applyTwice :: (a -> a) -> a -> a -- 注意這裏的括號不能省略
applyTwice f x = f (f x)
複製代碼

注意這裏的括號不能省略,由於 -> 默認是右結合。post

技巧:在編寫函數(尤爲是高階函數)時若是拿不許函數的類型,能夠先不寫函數的類型聲明,定義完畢後再利用 :t 查看 Haskell 推導出的結果。spa

工具箱

ghci> map (++ "!") ["BIFF"" BANG"" POW"]
["BIFF!"," BANG!"," POW!"]
ghci> filter even [1.. 10] 
[2, 4, 6, 8, 10]
ghci> let listOfFuns = map (*) [0..] 
ghci> (listOfFuns !! 4) 5 
20
複製代碼

lambda

lambda 就是一次性的匿名函數。寫法是\param1 param2 -> returnValue,必要的時候能夠用圓括號括起來。3d

若是參數是二元組,應該怎麼聲明 lambda:code

ghci> map (\(a, b) -> a + b) [(1, 2),( 3, 5)] -- 注意這裏的 (a,b) 是模式匹配,不是兩個參數
[3, 8]
複製代碼

如下例子說明 Haskell 的函數是默認柯里化的ip

addThree :: Int -> Int -> Int -> Int 
addThree x y z = x + y + z 

addThree' :: Int -> Int -> Int -> Int 
addThree' = \x -> \y -> \z -> x + y + z

-- 上面倆函數等價

flip1 :: (a -> b -> c) -> b -> a -> c 
flip1 f y x = f x y

flip2 :: (a -> b -> c) -> b -> a -> c 
flip2 f = \y x -> f x y

-- 上面倆函數等價
複製代碼

fold 摺疊(相似 reduce)

foldl (\acc x -> acc + x) init list
foldr (\x acc -> x : acc) init list
foldl1 step list -- 第一個元素就是 init
foldr1 step list -- 第一個元素就是 init
複製代碼

另外一種方式理解摺疊:摺疊就是對列表中的全部元素連續地應用某個函數。如:

foldr f 0 [1,2,3,4] 
-- 其實就是
f 1 (f 2 (f 3 (f 4 0)))

foldl g 0 [1,2,3,4]
-- 其實就是
g (g (g (g 0 1) 2) 3) 4
複製代碼

當二元函數 f 不老是須要對第二個參數求值時,便可經過 foldr 對無限列表作處理。fordl 則不行。

scan 與 fold 相似,區別在於 scan 會把每一次 step 的返回值記錄在一個列表裏。

$ 函數應用符號

前面的是函數, 後面的是函數的參數,相似 JS 的 .call。

sum $ filter (> 10) $ map (*2) [2.. 10] -- 獲得 80
sum (filter (> 10) (map (*2) [2.. 10])) -- 若是不用 $ 就要加不少括號

ghci> map ($ 3) [(4+),( 10*),(^ 2), sqrt]
[7. 0, 30. 0, 9. 0, 1. 7320508075688772]
複製代碼

函數組合 composition

數學定義:(f·g)(x) = f(g(x)),它的目的是方便生成新函數。

map (negate . sum . tail) [[1.. 5],[ 3.. 6],[ 1.. 7]] 
-- [-14,- 15,- 27] 
複製代碼

若是要組合的函數有多個參數,就只能化爲只有一個參數的函數(柯里化)再組合。

sum . replicate 5 $ max 6.7 8.9 --44.5
--等價於
sum . (replicate 5) $ max 6.7 8.9 -- 44.5
複製代碼

技巧:若是你打算用組合來省去大量括號,能夠先找出最裏面的函數和參數,寫下來,在前面加一個 ,而後把其餘部分組合起來寫在 前面,如

replicate 2 (product (map (*3) (zipWith max [1, 2] [4, 5])))
-- 改寫爲
replicate 2 . product . map (*3) $ zipWith max [1, 2] [4, 5]
複製代碼

point-free 風格

若是你發現函數定義裏出現

fn x = fn2 p1 x

那麼你能夠直接簡化成

fn = fn2 p1

這一點在《計算的本質》也講到了。

這樣寫的好處是,簡潔。

相關文章
相關標籤/搜索