Haskell入门(五)(续)高阶函数(higher-order functions)

参考教材:Learn You a Haskell for Great Good (http://learnyouahaskell.com/)

操作环境:Ubuntu下Linux64位虚拟机

python入门编程, 之后用c++学习数据结构,Haskell萌新。

由于对Haskell中一些词语的中文翻译并不了解,接下来的内容中重点名词将有英文和我理解的中文。


Chapter5主要内容(续)

匿名函数(Lambdas)

lambdas基础知识

lambdas是仅供单次使用的匿名函数。在写法上它比普通的函数更简单。在高阶函数的实现中有时会遇到它。适当使用lambdas函数可以增强代码的可读性。

匿名函数,顾名思义,就是函数没有名称。Haskell语言里的lambdas以\开头,后面跟参数,再接以->开头的函数体。

比较匿名函数和普通的函数,我们发现它们的结构类似,只是匿名函数中不需要进行函数声明和函数名称,以->符号替代了普通函数中的=。匿名函数同样支持样式匹配(pattern matching),区别是我们针对一个参数只能使用一次样式匹配。如果匹配失败且得不到解决,程序会报错终止。

lambdas示例


lambdas示例

这里是几个Lambdas函数的使用。右侧两个numLongChains函数放在一起作为对比,在Load文件时,第三个函数尚不存在。

列表折叠函数(fold)

列表折叠函数可用于实现按元素递归列表的操作。它需要三个参数的输入:二元函数,起始值,待折叠列表。从起始值开始,按照折叠顺序,依次将当前值与待折叠列表中的元素进行二元操作,直到列表遍历完毕。我们可以通过调用不同的函数来控制折叠的方式。

左折叠(fold left)

左折叠的实现

右折叠(fold right)

与左折叠原理类似,区别是从待折叠列表的右侧开始操作。这可能造成折叠函数的写法上与左折叠有区别:用于累计的accumulator写在右边。

右折叠的实现

在这里使用右折叠,而不使用左折叠的理由是,左折叠用到的++函数需要遍历元素,而右折叠用到的:函数只需要O(1)的复杂度。在列表规模较大时,右折叠有优势。

右折叠的另一个优势是,借助于Haskell laziness的特性,它可以针对一些无穷列表进行处理。这部分的具体内容建议参考Stack Overflow :foldl versus foldr behavior with infinite lists.下面是截图。

Stack Overflow上关于两种fold的区别的部分解释

折叠的一些例子


折叠的一些例子

相似:扫描函数(scan)

scan函数可以用来监控折叠的过程。同折叠一样,它也有scanl和scanr两种形式,要求二元函数操作,初始值,待扫面列表。区别是,它还会保留所有中间变量。具体例子如图:

扫描函数示例

除了上面提到的函数外,还有不需要初始值(默认为首个)foldl1, foldr1, scanl1, scanr1函数。这里暂时不详细介绍。

右结合(right-associative)函数操作符$

通常情况下,使用空格分隔实现的函数是左结合的,它们的优先级极高。下面要讲的函数操作符$却是右结合的,它有最低的优先级。代码中的$表明,这个符号右边的内容会先处理,处理完毕后作为参数传递给左边的函数。

这一符号的第一个作用是,利用它的右结合特性,减少代码中括号的出现,增强代码的可读性。

此外,这一符号还可以起到调用函数应用的作用(lets us treat function application like just another function)。可能在想法上有点类似于函数指针?

$操作符的两个作用

函数复合(function composition)

在Haskell里,我们使用.来实现函数复合。需要注意,f.g要求f的返回值和g的参数值有相同的类型。

由于函数复合符号.是右结合的,我们可以按照需要复合多个函数。

函数复合示例

如果我们希望处理的函数需要多个参数呢?我们可以运用前面的不完全函数来实现。

综合上面我们讲到的,如果我们想要处理简化一个多个括号的函数,我们可以从最内层开始写,在需要的地方使用$符号减少括号,使用不完全函数处理函数间的复合调用。

函数复合也可以在无点号(point-free)的情况下实现,具体参考前面函数部分的例子,在此不再赘述。

你可能感兴趣的:(Haskell入门(五)(续)高阶函数(higher-order functions))