Haskell入门篇九:高阶函数(下)
Posted Lambda小粽子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Haskell入门篇九:高阶函数(下)相关的知识,希望对你有一定的参考价值。
这一周带给大家的内容是如何在Haskell中写出和数学函数表达式一样顺序的函数,在这一篇文章中我将给大家介绍两个函数和两个operator,分别是id,flip,($) 和 (.)。
首先我们先来说一下id函数,id其实是identity的缩写,字面意思为恒等式。
从id函数的type可以看出,id函数只是简单将接收的函数return出去。可能大家和我刚刚认识id函数的时候一样,觉得这个函数好像没什么用处,而事实并非如此,在文章的后面的例子,还有之后的文章中,id函数将会被多次使用,到时候我会为大家一一讲解id函数在例子中的作用。
接下来我们来谈一下flip函数。flip这个函数很有意思,它接收一个函数,然后返回一个和这个函数运算结果相同但是传入变量顺序相反的函数。
注意之前我有提过函数的类型是右结合,也就是说flip的函数类型完全可以理解为flip :: (a -> b -> c) -> (b -> a -> c)(当然也可以理解为接收函数和两个变量并生成结果,但我更倾向于去理解为前者。接下来我来给大家举两个用flip的例子,第一个就是我们在上个文章中提到过的 (flip (:)) 函数,印象不深的读者建议回顾一下上一篇文章的内容。
我们能够得知这个函数的类型如图所示,当时使用flip的原因,是因为flip可以交换(:)操作符的两个变量的 传入顺序。由于我们的函数是先接收一个list然后接收一个element,顺序刚好与(:)相反,使用flip函数就可以应用我们之前说过的η化简(注意,在haskell中优雅的代码书写格式也是程序的一部分,由于ghc内部有优化,所以不用担心使用这些小的函数会影响程序的运行效率)。第二个例子是flip id。相信这个函数的类型跟大家想象的有所不一样,在这里我就利用这个函数组合来带大家来推断一下类型。
flip的类型是(a -> b -> c) -> b -> a -> c而id的类型是x -> x(为了方便区分),那么当flip apply在id函数上时,id函数就成为了flip接收的第一个变量,所以根据类型替换,(a -> b -> c)替换为x -> x,即flip中的a被替换成id中的x了,由于函数是右结合,所以b -> c也被替换成了x,所以我们得知 a == x,b -> c == x,,于是a和b -> c变为等价的类型,于是在之后的结果类型b -> a -> c中,a就被替换成了b -> c,所以才 会出现上面图中的类型(看,id函数就这么简单的被使用了,是不是并不是我们之前想的那样没什么用呢?)。
接下来我们来谈一谈($)这个操作符。这个操作符的用处主要体现如下。假如我们想定义这么一个函数f = g (h x),或者在我们定义的时候函数进行了多层嵌套,由于我们知道函数的运算方式是左结合,也就是g h x = (g h) x并不符合我们的初衷,所以我们需要加上很多的括号来保证函数运行的优先级。(但是由于加括号真的很麻烦)所以我们可以用$运算符来做到这一点,即 g (h x) = g $ h x。
$类型如上图所示,而其原理如下,由于$定义的时候被设置为向右结合且优先度最低,所以当我们将函数写为g $ h x时,h x由于是函数应用优先度高于$所以被优先计算,而其计算结果和g函数一起会被传入$操作符进行运算(通过类型我们得知 f $ x = f x,即使用$和将函数直接应用的结果是一样的),所以最后的结果就如我们所愿了。这样我们就可以写出像 f $ g $ h $ i $ j x这样的函数而避免了使用括号,如果我们想要将函数写成从左至右和pipe管道很相似的函数,我们可以简单地使用刚刚提到的flip函数与($)函数进行组合 flip ($),
由于函数运算的默认结合方式是左结合刚好符合我们的要求,所以我们接可以写成这样的函数。值得一提的是,大家能通过类型发现其实flip id和flip ($)的作用其实是一样的,有兴趣的同学可以继续研究下。
最后我们来提一下函数复合,还是之前我们的需求,如果我们能够在应用多个函数的时候将函数本身组合在一起,就会使很多定义变得更加的简洁明了。Haskell中提供了函数复合的操作符(.)。
通过函数的类型,我们可以如下使用这个函数f (g (h x)) = (f . g . h) x。所以如果我们想要定义一个函数是几种函数的复合的结果,我们可以定义为 f x = (g . h . i) x,然后通过η化简得到 f = (g . h . i)。这个技巧在Haskell中是极为常见的,由于在Haskell中我们很多时候就是使用函数来定义函数,所以函数复合的用处变得极为的广泛。
今天的内容就到这里,Haskell入门篇的内容也即将步入尾声,接下来我们将进入较为高级的Haskell技巧,敬请期待。
注:本文为原创内容,转载请标明出处。
以上是关于Haskell入门篇九:高阶函数(下)的主要内容,如果未能解决你的问题,请参考以下文章