高阶函数#
- 高阶函数:可接受函数作为参数、或可返回函数作为返回值的函数;
柯里化#
源于 λ 演算;
Haskell 中的函数严格来说只接受一个参数,通过柯里化可以实现多个参数的接受;
备注
Int -> Int -> Int
实际表达的意思为Int -> (Int -> Int)
,即接受一个整数,返回一个函数,该函数接受并返回一个整数。因为
->
为右结合,所以Int -> (Int -> Int)
等价于Int -> Int -> Int
。
部分应用#
部分应用:因为柯里化,当参数接受的实参少于形参时,函数会返回一个包含剩余形参的函数,这种用法称为部分应用;
partialAdd :: Num a => a -> a partialAdd = (+ 3) -- (+) :: Num a => a -> a -> a exp1 = partialAdd 5 -- 8
部分应用函数可以在执行函数时创造函数;
exp2 = filter (> 3) [1, 5, 3, 2, 1, 6, 4, 3, 2, 1] -- [5,6,4] exp3 = filter (`elem` ['A'..'Z']) "aLice In woNdErlanD" -- "LINED" exp4 = let listOfFuncs = map (*) [0 ..] in (listOfFuncs !! 4) 5 -- 20
注意
-
运算符是 Haskell 中唯一既可做前缀又可做中缀的运算符,因此(- 3)
会解释为负三,而非部分应用。若要部分应用减法,应使用subtract
函数。
Lambda 函数#
\<var> ... -> <functionBody>
Lambda 函数:匿名函数;
语法格式:
\
:定义一个 Lambda 函数;备注
\
实际上是 λ 的简写。x
:参数,多个参数之间用空格分隔;备注
\x y z -> ...
实际上等价于\x -> \y -> \z -> ...
。->
:定义函数体,默认向右无限扩展;
Lambda 函数支持模式匹配,但仅支持单个匹配,若不匹配则报错;
exp1 = map (\x -> 7 * x + 2) [1 .. 5] -- [9,16,23,30,37]
exp2 = zipWith (\a b -> (a * 30 + 3) / b) [1 .. 3] [4 .. 6]
-- [8.25,12.6,15.5]
exp3 = map (\(a, b) -> a + b) [(1, 2), (3, 5), (6, 3), (2, 6)]
-- [3,8,9,8]
函数组合#
-
GHC.Base.
(.)
:: (b -> c) -> (a -> b) -> a -> c# <function1> . <function2> . ...
函数组合,即将多个函数链式组合,使得上一个函数的结果作为下一个函数的输入值。
在数学上函数组合定义为 \((f\circ g)(x)=f(g(x))\),即 \(g(x)\) 的结果作为 \(f(x)\) 的自变量继续进行计算。
类型:第一个函数接受的参数类型必须与第二个函数的返回值类型相同,最终的返回值类型与第一个函数的返回值类型相同。
结合性:函数组合
.
默认为右结合,因此首先对第二个函数进行解析。exp1 = map (\x -> negate (abs x)) [5, -3, 7] -- [-5,-3,-7] exp2 = map (negate . abs) [5, -3, 7] -- [-5,-3,-7]
若有多参数函数,则必须先对函数进行部分应用,再组合函数。
exp3 = (sum . takeWhile (< 10000) . filter odd . map (^2)) [1 ..]
无值编程:当函数最右端为参数时,可省略该形参,返回一个函数;
sum' :: (Foldable t, Num b) => t b -> b sum' xs = foldl (+) 0 xs -- 此处 "xs" 不必要 sum' :: GHC.Types.Any Integer -> Integer sum' = foldl (+) 0 -- 返回接收列表的函数 fn :: (RealFrac a, Integral b, Floating a) => a -> b fn x = ceiling (negate (tan (cos (max 50 x)))) fn :: Double -> Integer fn = ceiling . negate . tan . cos . max 50 -- 返回一个函数
函数组合有时可以提高代码可读性,利于无值编程,但并不是任何时候函数组合都能提高可读性;
addSqSum :: Integer addSqSum = (sum . takeWhile (<10000) . filter odd . map (^ 2)) [1 ..] addSquareSum :: Integer -- 可能更易读 addSquareSum = let oddSquares = (filter odd . map (^ 2)) [1 ..] belowLimit = takeWhile (< 10000) oddSquares in sum belowLimit