函数式编程之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 numnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]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 numdef multiply_by_three(nums): for num in nums: yield num * 3def convert_to_string(nums): for num in nums: yield 'The Number: %s' % numnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]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 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]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的,我们重要的品味其中的味道。
参考
- Wikipedia: Functional Programming
- truly understanding the difference between procedural and functional
- A practical introduction to functional programming
- What is the difference between procedural programming and functional programming?
- Can someone give me examples of functional programming vs imperative/procedural programming?
- OOP vs Functional Programming vs Procedural
- Python – Functional Programming HOWTO
补充:评论中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()@Pipedef even_filter(num): return num if num % 2 == 0 else None@Pipedef multiply_by_three(num): return num*3@Pipedef convert_to_string(num): return 'The Number: %s' % num@Pipedef echo(item): print item return itemdef force(sqs): for item in sqs: passnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]force(nums | even_filter | multiply_by_three | convert_to_string | echo) |
函数式编程之pipeline——很酷有没有的更多相关文章
- Python函数式编程之map()
Python函数式编程之map() Python中map().filter().reduce()这三个都是应用于序列的内置函数. 格式: map(func, seq1[, seq2,…]) 第一个参数 ...
- python3 第二十一章 - 函数式编程之return函数和闭包
我们来实现一个可变参数的求和.通常情况下,求和的函数是这样定义的: def calc_sum(*args): ax = 0 for n in args: ax = ax + n return ax 但 ...
- python3 第二十章 - 函数式编程之Higher-order function(高阶函数)
什么是高阶函数?把函数作为参数传入或把函数做为结果值返回,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式.函数式编程的特点: 函数本身可以赋值给变量,赋值后变量为函数: 允许将函数本身 ...
- 函数式编程之foldLeftViaFoldRight
问题来自 Scala 函数式编程 一书的习题, 让我很困扰, 感觉函数式编程有点神学的感觉.后面看懂之后, 又觉得函数式编程所提供的高阶抽象是多么的强大. 这个问题让我发呆了好久, 现在把自己形成的想 ...
- Python函数式编程之lambda表达式
一:匿名函数的定义 lambda parameter_list: expression 二:三元表达式 条件为真时返回的结果 if 条件判断 else 条件为假的时候返回的结果 三:map map(f ...
- 函数式编程之-bind函数
Bind函数 Bind函数在函数式编程中是如此重要,以至于函数式编程语言会为bind函数设计语法糖.另一个角度Bind函数非常难以理解,几乎很少有人能通过简单的描述说明白bind函数的由来及原理. 这 ...
- 函数式编程之-F#类型系统
在深入到函数式编程思想之前,了解函数式独有的类型是非常有必要的.函数式类型跟OO语言中的数据结构截然不同,这也导致使用函数式编程语言来解决问题的思路跟OO的思路有明显的区别. 什么是类型?类型在编程语 ...
- 函数式编程之-Partial application
上一篇关于Currying的介绍,我们提到F#是如何做Currying变换的: let addWithThreeParameters x y z = x + y + z let intermediat ...
- 函数式编程之-Currying
这个系列涉及到了F#这门语言,也许有的人觉得这样的语言遥不可及,的确我几乎花了2-3年的时间去了解他:也许有人觉得学习这样的冷门语言没有必要,我也赞同,那么我为什么要花时间去学习呢?作为一门在Tiob ...
随机推荐
- HTML5 VUE单页应用 SEO 优化之 预渲染(prerender-spa-plugin)
前言:当前 SPA 架构流行的趋势如日中天,前后端分离的业务模式已经成为互联网开发的主流方式,但是 单页面 应用始终存在一个痛点,那就是 SEO, 对于那些需要推广,希望能在百度搜索时排名靠前的网站而 ...
- DApp是什么,DApp是必然趋势
DApp是什么,DApp是必然趋势 https://www.jianshu.com/p/dfe3098de0de Thehrdertheluck关注 12018.04.23 11:54:00字数 2 ...
- Linux 就该这么学 CH03 管道符、重定向和环境变量
0 概述 1 输入输出重定向 重定向技术有5种模式: 标准覆盖输出重定向 标准追加输出重定向 错误覆盖输出重定向 错误追加输出重定向 输入重定向 输入重定向是将文件导入到命令中. 输出重定向是将输入到 ...
- Ubuntu下重启mysql
启动mysql: 方式一:sudo /etc/init.d/mysql start 方式二:sudo service mysql start 停止mysql: 方式一:sudo /etc/init.d ...
- PowerDNS + PowerDNS-Admin
一.基础配置 1.1 环境说明 Centos 7.5.1804 PDNS MariaDB 1.2 关闭防火墙和 selinux setenforce sed -i 's/SELINUX=enforci ...
- Java的集合类之 map 接口用法
Map接口不是Collection接口的继承.而是从自己的用于维护键-值关联的接口层次结构入手.按定义,该接口描述了从不重复的键到值的映射. 我们可以把这个接口方法分成三组操作:改变.查询和提供可选视 ...
- 使用 Redis 的 sorted set 实现用户排行榜
要求:实现一个用户排行榜,用户数量有很多,排行榜存储的是用户玩游戏的分数,对排行榜的读取压力比较大,如何实现? 思路分析: 实现排行榜,可以考虑使用 Redis 的 zset 结构: 用户数量很多的话 ...
- tensorflow-笔记02
TensorFlow扩展功能 自动求导.子图的执行.计算图控制流.队列/容器 1.TensorFlow自动求导 在深度学习乃至机器学习中,计算损失函数的梯度是最基本的需求,因此TensorFlow也原 ...
- 《三》大话 Typescript 接口
> 前言: 本文章为 TypeScript 系列文章. 旨在利用碎片时间快速入门 Typescript. 或重新温故 Typescript 查漏补缺.在官方 api 的基础上, 加上一些日常使用 ...
- SqlServer调用OPENQUERY函数远程执行增删改查
/* OPENQUERY函数,远程执行数据库增删改查 关于OPENQUERY函数第二个参数不支持拼接变量的方案 方案1:将OPENQUERY语句整个拼接为字符串,再用EXEC执行该字符串语句 方案2: ...