柯里函数,高阶函数举例
柯里函数,高阶函数举例
最近比较忙,好长时间不写博客了,先写一个Haskell凑个数
参考书籍: Learn you a haskell
柯里函数
本质上,Haskell的所有函数都只有一个参数,我们所见过的所有多参数的函数都是柯里函数。柯里函数不会一次性取完所有参数,而是在每次调用时只娶一个参数,并返回一个一元函数来取下一个参数,以此类推。比如max函数,我们常如下使用: 1
ghci> max 4 5
4传入max函数,组成一个一元函数,然后再传入5,也就是说,上面的式子其实为(max 4) 5。
那么,这么做的意义何在呢?有了柯里函数,我们便可以以部分参数调用一个函数来得到一个部分应用(partial application)。比如我们拥有一个将三数想乘的函数: 1
2
3
4
5
6
7
8
9multiplyThree :: Int -> Int -> Int -> Int
--类型也可以写成如下形式
multiplyThree :: Int -> (Int -> (Int -> Int))
--我们可以定义一个函数如下
multiplyTwoWithNine = multiplyThree 9
--然后我们便可以如下使用
multiplyTwoWithNine 3 5
截断
通过截断,我们也能够对中缀函数进行部分应用。将一个参数放在中缀函数的一侧,并在外面用括号括起,便可以截断这个中缀函数,得到一个一元函数。如下: 1
2divideByTen :: (floating a) => a -> a
divideByTen = (/10)-运算符的截断,但是为了方便起见,Haskell会把它认为是负4,所以我们应该使用(substract 4)。
高阶函数举例
高阶函数指的是可以将函数作为传入参数和返回值的函数,很明显,Haskell中的函数都为高阶函数。现在,我们写一个函数,他能够取一个函数,并针对某个值应用两次: 1
2applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)applyTwice的声明中用到了括号,这表明我们传入的是一个(a -> a)类型(即一元函数)而非两个a类型。否则我们调用applyTwice f x时编译器会认为我们想要传入的是两个a类型的常量,返回了一个a -> a类型的函数。
借助部分应用与高阶函数,我们可以轻松实现以下函数: 1
2
3
4ghci> applyTwice ("HAHA " ++) "HEY"
"HAHA HAHA HEY"
ghci> applyTwice (multThree 2 2) 9
144
为了举几个常用的高阶函数的例子,我们来是新一下在Haskell中常用的一个高阶函数zipWith。它取一个二元函数和两个列表作为参数,对这两个列表中对应的元素调用该二元函数,最终返回一个列表。 1
2
3
4zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
wipWith' f (x:xs) (y:ys) = f x y : zipWith' xs ys
实现flip
接下来实现标准库中的另一个函数flip。flip函数去一个函数作为参数,返回一个效果相同的函数。两个函数不同的地方在于函数的前两个参数的位置发生了颠倒。 1
2
3
4
5flip' :: (a -> b -> c) -> (b -> a -> c)
flip' f = g
truewhere g x y = f y x
--或者使用如下写法
flip' g y x = f x y