加入收藏 | 设为首页 | 会员中心 | 我要投稿 好传媒网 (https://www.haochuanmei.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 移动互联 > 评测 > 正文

手把手介绍函数式编程:从命令式重构到函数式

发布时间:2019-09-11 10:18:35 所属栏目:评测 来源:佚名
导读:本文是一篇手把手的函数式编程入门介绍,借助代码示例讲解细腻。但又不乏洞见,第一节中列举和点评了函数式种种让眼花缭乱的特质,给出了『理解函数式特质的指南针:函数式代码的核心特质就一条,无副作用』,相信这个指南针对于有积极学过挖过函数式的同

与下面实现对比一下:

  1. print pipeline_each(bands, [set_canada_as_country, 
  2.                             strip_punctuation_from_name, 
  3.                             capitalize_names]) 

这段代码很容易理解。给人的印象是辅助函数是函数式的,因为它们看过来是串联在一起的。前一个函数的输出成为下一个的输入。如果是函数式的,就很容易验证。也易于重用、易于测试且易于并行化。

pipeline_each() 的功能就是将乐队一次一个地传递给一个转换函数,比如 set_canada_as_country() 。将转换函数应用于所有乐队后, pipeline_each() 将转换后的乐队打包起来。然后,打包的乐队传递给下一个转换函数。

我们来看看转换函数。

  1. def assoc(_d, key, value): 
  2.     from copy import deepcopy 
  3.     d = deepcopy(_d) 
  4.     d[key] = value 
  5.     return d 
  6.  
  7. def set_canada_as_country(band): 
  8.     return assoc(band, 'country', "Canada") 
  9.  
  10. def strip_punctuation_from_name(band): 
  11.     return assoc(band, 'name', band['name'].replace('.', '')) 
  12.  
  13. def capitalize_names(band): 
  14.     return assoc(band, 'name', band['name'].title()) 

每个函数都将乐队的一个键与一个新值相关联。如果不变更原乐队,没有简单的方法可以直接实现。 assoc() 通过使用 deepcopy() 生成传入字典的副本来解决此问题。每个转换函数都对副本进行修改并返回该副本。

一切似乎都很好。当键与新值相关联时,可以保护原乐队字典免于被变更。但是上面的代码中还有另外两个潜在的变更。在 strip_punctuation_from_name() 中,原来的乐队名通过调用 replace() 生成无标点的乐队名。在 capitalize_names() 中,原来的乐队名通过调用 title() 生成大写乐队名。如果 replace() 和 title() 不是函数式的,则 strip_punctuation_from_name()和 capitalize_names() 也将不是函数式的。

幸运的是, replace() 和 title() 不会变更他们操作的字符串。这是因为字符串在 Python中是不可变的( immutable )。例如,当 replace() 对乐队名字符串进行操作时,将复制原来的乐队名并在副本上执行 replace() 调用。Phew~有惊无险!

Python 中字符串和字典之间在可变性上不同的这种对比彰显了像 Clojure 这样语言的吸引力。 Clojure 程序员完全不需要考虑是否会改变数据。 Clojure 的数据结构是不可变的。

练习4:尝试编写 pipeline_each 函数的实现。想想操作的顺序。数组中的乐队一次一个传递到第一个变换函数。然后返回的结果乐队数组中一次一个乐队传递给第二个变换函数。以此类推。

我的实现方案:

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

所有三个转换函数都可以归结为对传入的乐队的特定字段进行更改。可以用 call() 来抽象, call() 传入一个函数和键名,用键对应的值来调用这个函数。

  1. set_canada_as_country = call(lambda x: 'Canada', 'country') 
  2. strip_punctuation_from_name = call(lambda x: x.replace('.', ''), 'name') 
  3. capitalize_names = call(str.title, 'name') 
  4.  
  5. print pipeline_each(bands, [set_canada_as_country, 
  6.                     strip_punctuation_from_name, 
  7.                     capitalize_names]) 

(编辑:好传媒网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读