陈两岁|函数式编程( 四 )


上面的代码依然把程序的逻辑分成了函数 , 不过这些函数都是functional的 。 因为它们有三个症状:
1)它们之间没有共享的变量 。
2)函数间通过参数和返回值来传递数据 。
3)在函数里没有临时变量 。
我们还可以看到 , for循环被递归取代了(见race函数)——递归是函数式编程中带用到的技术 , 正如前面所说的 , 递归的本质就是描述问题是什么 。
陈两岁|函数式编程
文章图片
Pipeline
pipeline管道借鉴于UnixShell的管道操作——把若干个命令串起来 , 前面命令的输出成为后面命令的输入 , 如此完成一个流式计算 。 (注:管道绝对是一个伟大的发明 , 他的设哲学就是KISS–让每个功能就做一件事 , 并把这件事做到极致 , 软件或程序的拼装会变得更为简单和直观 。 这个设计理念影响非常深远 , 包括今天的WebService , 云计算 , 以及大数据的流式计算等等)
比如 , 我们如下的shell命令:
psauwwx|awk'{print$2}'|sort-n|xargsecho
如果我们抽象成函数式的语言 , 就像下面这样:
xargs(echo,sort(n,awk('print$2',ps(auwwx))))
也可以类似下面这个样子:
pids=for_each(result,[ps_auwwx,awk_p2,sort_n,xargs_echo])
好了 , 让我们来看看函数式编程的Pipeline怎么玩?
我们先来看一个如下的程序 , 这个程序的process有三个步骤:
1)找出偶数 。
2)乘以3
3)转成字符串返回
defprocess(num):#filteroutnon-evensifnum%2!=0:returnnum=num*3num='TheNumber:%s'%numreturnnumnums=[1,2,3,4,5,6,7,8,9,10]fornuminnums:printprocess(num)#输出:#None#TheNumber:6#None#TheNumber:12#None#TheNumber:18#None#TheNumber:24#None#TheNumber:30
我们可以看到 , 输出的并不够完美 , 另外 , 代码阅读上如果没有注释 , 你也会比较晕 。 下面 , 我们来看看函数式的pipeline(第一种方式)应该怎么写?
defeven_filter(nums):fornuminnums:ifnum%2==0:yieldnumdefmultiply_by_three(nums):fornuminnums:yieldnum*3defconvert_to_string(nums):fornuminnums:yield'TheNumber:%s'%numnums=[1,2,3,4,5,6,7,8,9,10]pipeline=convert_to_string(multiply_by_three(even_filter(nums)))fornuminpipeline:printnum#输出:#TheNumber:6#TheNumber:12#TheNumber:18#TheNumber:24#TheNumber:30
我们动用了Python的关键字yield , 这个关键字主要是返回一个Generator , yield是一个类似return的关键字 , 只是这个函数返回的是个Generator-生成器 。 所谓生成器的意思是 , yield返回的是一个可迭代的对象 , 并没有真正的执行函数 。 也就是说 , 只有其返回的迭代对象被真正迭代时 , yield函数才会正真的运行 , 运行到yield语句时就会停住 , 然后等下一次的迭代 。 (这个是个比较诡异的关键字)这就是lazyevluation 。
好了 , 根据前面的原则——“使用Map&Reduce , 不要使用循环” , 那我们用比较纯朴的Map&Reduce吧 。
defeven_filter(nums):returnfilter(lambdax:x%2==0,nums)defmultiply_by_three(nums):returnmap(lambdax:x*3,nums)defconvert_to_string(nums):returnmap(lambdax:'TheNumber:%s'%x,nums)nums=[1,2,3,4,5,6,7,8,9,10]pipeline=convert_to_string(multiply_by_three(even_filter(nums)))fornuminpipeline:printnum
但是他们的代码需要嵌套使用函数 , 这个有点不爽 , 如果我们能像下面这个样子就好了(第二种方式) 。
pipeline_func(nums,[even_filter,multiply_by_three,convert_to_string])
那么 , pipeline_func实现如下:
defpipeline_func(data,fns):returnreduce(lambdaa,x:x(a),fns,data)