zoukankan      html  css  js  c++  java
  • 函数式编程之pipeline——很酷有没有

    Pipeline

    pipeline 管道借鉴于Unix Shell的管道操作——把若干个命令串起来,前面命令的输出成为后面命令的输入,如此完成一个流式计算。(注:管道绝对是一个伟大的发明,他的设哲学就是KISS – 让每个功能就做一件事,并把这件事做到极致,软件或程序的拼装会变得更为简单和直观。这个设计理念影响非常深远,包括今天的Web Service,云计算,以及大数据的流式计算等等)

    比如,我们如下的shell命令:

    1
    ps auwwx | awk '{print $2}' sort -n | xargs echo

    如果我们抽象成函数式的语言,就像下面这样:

    1
    xargs(  echo, sort(n, awk('print $2', ps(auwwx)))  )

    也可以类似下面这个样子:

    1
    pids = for_each(result, [ps_auwwx, awk_p2, sort_n, xargs_echo])

    好了,让我们来看看函数式编程的Pipeline怎么玩?

    我们先来看一个如下的程序,这个程序的process()有三个步骤:

    1)找出偶数。
    2)乘以3
    3)转成字符串返回

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    def process(num):
        # filter out non-evens
        if num % 2 != 0:
            return
        num = num * 3
        num = 'The Number: %s' % num
        return num
     
    nums = [12345678910]
     
    for num in nums:
        print process(num)
     
    # 输出:
    # None
    # The Number: 6
    # None
    # The Number: 12
    # None
    # The Number: 18
    # None
    # The Number: 24
    # None
    # The Number: 30

    我们可以看到,输出的并不够完美,另外,代码阅读上如果没有注释,你也会比较晕。下面,我们来看看函数式的pipeline(第一种方式)应该怎么写?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    def even_filter(nums):
        for num in nums:
            if num % 2 == 0:
                yield num
    def multiply_by_three(nums):
        for num in nums:
            yield num * 3
    def convert_to_string(nums):
        for num in nums:
            yield 'The Number: %s' % num
     
    nums = [12345678910]
    pipeline = convert_to_string(multiply_by_three(even_filter(nums)))
    for num in pipeline:
        print num
    # 输出:
    # The Number: 6
    # The Number: 12
    # The Number: 18
    # The Number: 24
    # The Number: 30

    我们动用了Python的关键字 yield,这个关键字主要是返回一个Generator,yield 是一个类似 return 的关键字,只是这个函数返回的是个Generator-生成器。所谓生成器的意思是,yield返回的是一个可迭代的对象,并没有真正的执行函数。也就是说,只有其返回的迭代对象被真正迭代时,yield函数才会正真的运行,运行到yield语句时就会停住,然后等下一次的迭代。(这个是个比较诡异的关键字)这就是lazy evluation。

    好了,根据前面的原则——“使用Map & Reduce,不要使用循环”,那我们用比较纯朴的Map & Reduce吧。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def even_filter(nums):
        return filter(lambda x: x%2==0, nums)
     
    def multiply_by_three(nums):
        return map(lambda x: x*3, nums)
     
    def convert_to_string(nums):
        return map(lambda x: 'The Number: %s' % x,  nums)
     
    nums = [12345678910]
    pipeline = convert_to_string(
                   multiply_by_three(
                       even_filter(nums)
                   )
                )
    for num in pipeline:
        print num

    但是他们的代码需要嵌套使用函数,这个有点不爽,如果我们能像下面这个样子就好了(第二种方式)。

    1
    2
    3
    pipeline_func(nums, [even_filter,
                         multiply_by_three,
                         convert_to_string])

    那么,pipeline_func 实现如下:

    1
    2
    3
    4
    def pipeline_func(data, fns):
        return reduce(lambda a, x: x(a),
                      fns,
                      data)

    好了,在读过这么多的程序后,你可以回头看一下这篇文章的开头对函数式编程的描述,可能你就更有感觉了。

    最后,我希望这篇浅显易懂的文章能让你感受到函数式编程的思想,就像OO编程,泛型编程,过程式编程一样,我们不用太纠结是不是我们的程序就是OO,就是functional的,我们重要的品味其中的味道

    参考

    补充:评论中redraiment这个评论大家也可以读一读。

    感谢谢网友S142857 提供的shell风格的python pipeline:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    class Pipe(object):
        def __init__(self, func):
            self.func = func
     
        def __ror__(self, other):
            def generator():
                for obj in other:
                    if obj is not None:
                        yield self.func(obj)
            return generator()
     
    @Pipe
    def even_filter(num):
        return num if num % 2 == 0 else None
     
    @Pipe
    def multiply_by_three(num):
        return num*3
     
    @Pipe
    def convert_to_string(num):
        return 'The Number: %s' % num
     
    @Pipe
    def echo(item):
        print item
        return item
     
    def force(sqs):
        for item in sqs: pass
     
    nums = [12345678910]
     
    force(nums | even_filter | multiply_by_three | convert_to_string | echo)
  • 相关阅读:
    (转)classload和class.forname()区别
    (转)HashMap和HashTable源码
    (转)spring 框架介绍
    [Spring入门学习笔记][创建网站URL]
    [spring入门学习笔记][spring的IoC原理]
    [J2EE学习][post,get乱码处理]
    [J2EE框架][Debug]
    [SQL学习笔记][用exists代替全称量词 ]
    [Spring入门学习笔记][Spring Boot]
    [Spring入门学习笔记][maven]
  • 原文地址:https://www.cnblogs.com/bonelee/p/11235955.html
Copyright © 2011-2022 走看看