《Haskell趣學指南》筆記之高階函式
系列文章
- 《Haskell趣學指南》筆記之基本語法
- 《Haskell趣學指南》筆記之型別(type)
- 《Haskell趣學指南》筆記之函式
- 《Haskell趣學指南》筆記之高階函式
- 《Haskell趣學指南》筆記之模組
- 《Haskell趣學指南》筆記之自定義型別
一個函式滿足以下任一條件即為高階函式
- 接受函式作為引數
- 將函式作為返回值
《計算的本質》講得更好一些,我感覺這本書這裡講得不通透。
柯里化
讓函式只接受一個引數就夠了。 如果想接受兩個引數,就先接受一個,返回一個接受另一個引數的函式即可。
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) 複製程式碼
注意這裡的括號不能省略,因為 -> 預設是右結合。
技巧:在編寫函式(尤其是高階函式)時如果拿不準函式的型別,可以先不寫函式的型別宣告,定義完畢後再利用 :t 檢視 Haskell 推匯出的結果。
工具箱
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
,必要的時候可以用圓括號括起來。
如果引數是二元組,應該怎麼宣告 lambda:
ghci> map (\(a, b) -> a + b) [(1, 2),( 3, 5)] -- 注意這裡的 (a,b) 是模式匹配,不是兩個引數 [3, 8] 複製程式碼
以下例子說明 Haskell 的函式是預設柯里化的
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] 複製程式碼