5

Python函数式编程系列002:水管模型和compose

 2 years ago
source link: https://segmentfault.com/a/1190000040730126
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Python函数式编程系列002:水管模型和compose

这一章,我们要回到一个问题,到底函数式和过程式的编程思路到底在哪里?我们这里提供一个形象的比喻。

过程式--屋子物件

过程式思维里,每一个可变变量、函数/过程指称的符号,类似告诉你一个屋子的名字。对一个静态语言来说,我们可能还需要屋子里只能放什么东西。然后,我们每一次调用一次函数/过程,就是将对对应屋子里的函数取出来,以及其他屋子里的物件取出来,按照函数的方式重新整理,把结果放到原先的屋子或者新的屋子里。(注意这个描述和图灵机其实是类似的。)

但是,这个屋子可能会更复杂,我们可能有时候会参考别的屋子里的情况,甚至另一栋房子里的屋子(其他模块、第三方插件),或者甚至是天气和社会新闻(环境变量、硬件)来盘算每一次整理物件的逻辑(即函数)。这个是「屋子-物件」模型中,最让人困惑的事。如果你天气不好,或者别的屋子的情况有问题,你的整理物件的规则可能就有很大的问题,而且溯因是困难的。这个我们也在001中介绍过这个问题。

函数式--水管和数据流

在函数式编程中,我们的模型是编造一些列的水管,水管就是函数式中的函数。我们的目标就是事先将各种函数水管给架设好管道系统。然后将水(数据/不可变参数)倒入进去,等待水管的另一头流出结果就好了。就像下面的图显示的那样:

function

compose

我们到目前为止,可以想到的最简单的就是将水管相连。比如,在做文本处理的时候,我们很有可能会有以下的操作。这个就是典型的水管拼接的过程,我们只需要维护分词变小写删除stopword词干化这几个函数即可。

删除stopwords

当然,我们可以一步步把水倒到水管里,取出来再倒到另一个水管。那何不我们就直接事先帮水管串起来。这个操作也被称为compose(用符号∘\circ∘表示),数学表述如下:

(f∘g)x=f(g(x))(f \circ g) x = f(g(x))(f∘g)x=f(g(x))

我们给出一个简单的Python实现:

from functools import reduce

def compose(*args):
    """数学中的compose

    >>> from fppy.base import compose
    >>> compose(lambda x: x+1, lambda x: x**2)(1)
    >>> 4

    """
    return reduce(lambda f, g: lambda x: f(g(x)), args, lambda x: x)

比如我们就可以把下面的f1f2f3给串起来了:

>>> f1 = lambda x: x + 1
>>> f2 = lambda y: y * 2
>>> f3 = lambda z: z / 3

>>> compose(f3, f2, f1)(1)
1.3333333333333333

>>> h(g(f(1)))
1.3333333333333333

不过有时候,compose的顺序会让人困惑,我个人喜欢下面and_then的表述:

def and_then(*args):
    return reduce(lambda f, g: lambda x: g(f(x)), args)

对我个人而言这样子会更明确:

>>> and_then(f, g, h)(1)
1.3333333333333333

不过在具体实现里,我们其实用到了作为参数的函数的概念,这个也是函数式编程中「函数是一等公民」的表现,具体各种水管模式(听起来我们是长胡子的法国水管工)我们将在下面的文章中一一展现。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK