《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

这一点在《计算的本质》也讲到了。

这样写的好处是,简洁。

相关文章
相关标签/搜索