光伏发电逆变器价格系统的电表、逆变器的按装位置选择?

Haskell函数式编程入门_百度百科
Haskell函数式编程入门
《Haskell函数式编程入门》是一本讲解Haskell这门经过精心设计和锤炼的纯函数式编程语言的书,同时也是一本通过Haskell来讲解函数式编程的方法与思想的书。全书共分三个部分。第一部分介绍函数式编程在解决数学与算法问题的精简与直观的特色,让不熟悉Haskell的读者对其建立初步的了解,同时通过解决一些算法问题,如裴波那契数列、八皇后问题、排序问题、24点等,引发一些对函数式编程方式的思考;第二部分介绍一些略微深入的Haskell内容,包括函子、Monoid、IO与Monad转换器等;最后一部分则涉及快速测试、惰性求值和并行编程等主题。 《Haskell函数式编程入门》既适合对Hasell和函数式编程感兴趣的程序员阅读,又适合作为Haskell语言入门教程,供计算机科学与数学专业的学生参考。
Haskell函数式编程入门基本介绍
Haskell函数式编程入门内容简介
通过有趣的数学与算法问题来学习Haskell语言   内容循序渐进,由浅入深   例子相对简短,有助于选择性的阅读   对于函数编程中的Monad做了细致的讨论   部分术语给出了中英文对照,有助于查阅相关英文文献
Haskell函数式编程入门作者简介
张淞,1989年1月出生于黑龙江省绥芬河市,酷爱数学、物理,进入大学后开始喜欢学习与研究各类编程语言,并成为了一名Haskell爱好者。2012年7月于英国诺丁汉大学获得计算机科学本科学位。2013年11月于英国牛津大学获得计算机科学硕士学位。目前喜欢学习与研究λ演算、类型系统、抽象代数、范畴论、逻辑证明、组合数学等内容。
Haskell函数式编程入门图书目录
第1章 Haskell简介   1.1 Haskell的由来   1.2 Haskell编译器的安装以及 编写环境   1.3 GHCi的使用   1.3.1 GHCi中的命令   1.3.2 在GHCi中调用函数   1.4 .hs和.lhs文件、注释与库函数   1.5 第一个Haskell程序HelloWorld!   本章小结   第2章 类型系统和函数   2.1 Haskell的类型与数据   2.1.1 Haskell常用数据类型   2.1.2 函数类型   2.1.3 类型的别名   2.1.4 类型的重要性   2.2 Haskell中的类型类   2.2.1 相等类型类:Eq   2.2.2 有序类型类:Ord   2.2.3 枚举类型类:Emum   2.2.4 有界类型类:Bounded   2.2.5 数字类型类:Num   2.2.6 可显示类型类:Show   2.2.7 小结   2.3 Haskell中的函数   2.3.1 Haskell中的值   2.3.2 函数思想入门   2.3.3 函数的基本定义格式   2.3.4 λ表达式   2.3.5 参数的绑定   2.4 Haskell中的表达式   2.4.1 条件表达式   2.4.2 情况分析表达式   2.4.3 守卫表达式   2.4.4 模式匹配   2.4.5 运算符与函数   2.4.6 运算符与自定义运算符   本章小结   第3章 基于布尔值的函数   3.1 关键字module与import简介   3.2 简易布尔值的函数   3.3 与非门和或非门   本章小结   第4章 库函数及其应用   4.1 预加载库函数   4.1.1 常用函数   4.1.2 基于列表的函数   4.1.3 定义历法公式   4.1.4 字符串处理的函数   4.2 字符与位函数库简介   4.2.1 Data.Char   4.2.2 Data.Bits   本章小结   第5章 递归函数   5.1 递归函数的概念   5.2 简单递归函数   5.3 扩展递归与尾递归   5.4 互调递归   5.5 麦卡锡的91函数   5.6 斐波那契数列   5.7 十进制数字转成罗马数字   5.8 二分法查找   5.9 汉诺塔   5.10 排序算法   5.10.1 插入排序   5.10.2 冒泡排序   5.10.3 选择排序   5.10.4 快速排序   5.10.5 归并排序   小结   5.11 递归基本条件与程序终止   5.12 递归与不动点   5.13 无基本条件递归和惰性求值   本章小结   第6章 列表内包   6.1 列表生成器   6.2 素数相关趣题   6.3 凯撒加密   6.3.1 加密   6.3.2 解密   6.4 排列与组合问题   6.4.1 排列问题   6.4.2 错位排列问题   6.4.3 组合问题   6.5 八皇后问题   6.6 计算矩阵乘法   6.7 最短路径算法与矩阵乘法   本章小结   第7章 高阶函数与复合函数   7.1 简单高阶函数   7.2 折叠函数foldr与foldl   7.3 mapAccumL与mapAccumR函数   7.4 复合函数   本章小结   第8章 定义数据类型   8.1 数据类型的定义   8.1.1 枚举类型   8.1.2 构造类型   8.1.3 参数化类型   8.1.4 递归类型   8.1.5 杂合定义类型   8.2 类型的同构   8.3 使用newtype定义类型   8.4 数学归纳法的有效性   8.5 树   8.6 卡特兰数问题   8.7 霍夫曼编码   8.8 解24点   8.9 zipper   8.10 一般化的代数数据类型   8.11 类型的kind   8.11.1 类型的kind   8.11.2 空类型的声明   本章小结   第9章 定义类型类   9.1 定义类型类   9.2 Haskell中常见类型类   9.2.1 常用类型类   9.2.2 Functor   9.2.3 Applicative   9.2.4 Alternative   9.2.5 简易字符识别器   9.2.6 Read类型类   9.2.7 单位半群(Monoid)   9.2.8 Foldable与Monoid类型类   9.2.9 小结   9.3 类型类中的类型依赖   9.4 类型类中的关联类型   9.5 定长列表   9.6 运行时重载   9.7 Existential类型   本章小结   第10章 Monad初步   10.1 Monad简介   10.2 从Identity Monad开始   10.3 Maybe Monad   10.4 Monad定律   10.5 列表Monad   10.6 Monad相关运算符   10.7 MonadPlus   10.8 Functor、Applicative与Monad的关系   本章小结   第11章 系统编程及输入/输出   11.1 不纯函数与副作用   11.2 IO Monad   11.3 输入/输出处理   11.3.1 Control.Monad中的函数   11.3.2 系统环境变量与命令行参数   11.3.3 数据的读写   11.3.4 格式化输出printf函数   11.3.5 printf函数的简易实现   11.4 星际译王词典   11.4.1 二分法查找   11.4.2 散列表的使用   11.5 简易异常处理   11.6 Haskell中的时间   本章小结   ……   第12章 记录器Monad、读取器Monad、状态Monad   第13章 Monad转换器   第14章 QuickCheck简介   第15章 惰性求值简介   第16章 并行与并发编程   参考文献   后记踏出理解函数式编程概念的第一步是最重要的一步,有时也是最难的一步。不过也不一定,取决于你们的思考方式。
如果你还记得Part 3的内容,我们在组合 mult5 和 add 的时候遇到了问题,因为 mult5 接收一个参数而 add 接收两个。
只要限制所有函数都只接收一个参数,就可以轻松地解决这个问题。
相信我。这没听上去那么糟。
我们创建一个 add 函数,它接收两个参数,但是每次只接收一个参数。柯里化函数允许我们这么做。
柯里化函数是一种每次只接收一个参数的函数。
它允许我们在 add 与 mult5 组合前先传入它的第一个参数。
JavaScript里,我们可以这样实现 add 函数:
var add = x =& y =& x + y
这个版本的 add 是一个先接收一个参数,之后再接收另一个参数的函数。
具体来说,这个 add 接收单一参数 x ,然后返回一个接收单一参数 y 的函数,这个函数最终返回 x 和 y 的加和。
现在可以用这个版本的 add 来构建一个可用的 mult5AfterAdd10 了:
var compose = (f, g) =& x =& f(g(x));var mult5AfterAdd10 = compose(mult5, add(10));
compose 函数接收两个参数, f 和 g 。返回一个接收一个参数 x 的函数, x 会先传入 g 执行后,结果再传入 f 。
实际上我们做了什么?我们将旧的 add 函数转换成了柯里化版本。让 add 更加灵活,因为它的第一个参数 10 可以提前传入,最后一个参数可以在 mult5AfterAdd10 调用时再传入。
现在,你可能想知道在 Elm 中这个 add 函数要如何重写。事实证明,根本不需要重写。在 Elm 和其它函数式编程语言中,所有函数都是自动柯里化的。
所以 add 函数看上去和之前一样:
Part 3 中的 mult5AfterAdd10 需要这样重写:
mult5AfterAdd10 =
(mult5 && add 10)
从语法上讲, Elm 要优于像JavaScript这样的指令式语言,因为它对柯里化,函数组合等函数式的东西做了优化。
柯里化和重构
柯里化的另一个好处则体现在重构时,创建一个接收多参数的通用版函数,然后在需要用的地方通过传入部分参数创建出指定版本的函数。
举个例子,当我们需要下面两个将括号和双括号添加在字符串两侧的函数时:
bracket str =
&{& ++ str ++ &}&doubleBracket str =
&{{& ++ str ++ &}}&
我们需要这么用:
bracketedJoe =
bracket &Joe&doubleBracketedJoe =
doubleBracket &Joe&
我们可以将 bracket 和 doubleBracket 通用化:
generalBracket prefix str suffix =
prefix ++ str ++ suffix
但现在每次调用 generalBracket 时都必须将括号传入:
bracketedJoe =
generalBracket &{& &Joe& &}&doubleBracketedJoe =
generalBracket &{{& &Joe& &}}&
我们需要两全其美的办法。
如果把 generalBracket 的参数重新排序,我们可以利用函数自动柯里化的特点创建出 bracket 和 doubleBracket :
generalBracket prefix suffix str =
prefix ++ str ++ suffixbracket =
generalBracket &{& &}&doubleBracket =
generalBracket &{{& &}}&
注意,将静态参数( prefix 和 suffix )放在参数列表靠前的位置,可能改变的参数( str )放在后面,就可以轻松地创建出指定版本的 generalBracket 。
参数顺序对完全柯里化很重要。
还有一点, bracket 和 doubleBracket 是 point-free 模式的,即 str 参数是隐式的。 bracket 和 doubleBracket 都在等待它们的最后一个参数。
现在可以像之前一样使用了:
bracketedJoe =
bracket &Joe&doubleBracketedJoe =
doubleBracket &Joe&
但这次我们使用了通用化的柯里化函数 generalBracket 。
常用的功能函数
让我们看一下函数式语言里最常用的三个函数。
但在那之前,先看一眼下面这段JavaScript代码:
for (var i = 0; i & something. ++i) {
// do stuff}
这段代码有一个很严重的问题。但不是bug。问题在于这段代码其实是模板代码,即一遍一遍被重复的代码。
如果你用指令式语言,像Java, C#, JavaScript, PHP, Python等,就会发现这样的模板代码到处都是。
这就是它的错误所在。
所以让我们扼杀它。
从改变数组 things 开始:
var things = [1, 2, 3, 4];for (var i = 0; i & things. ++i) {
things[i] = things[i] * 10; // MUTATION ALERT !!!!}console.log(things); // [10, 20, 30, 40]
呃!!突变!
再试一次。这次我们不改变东西:
var things = [1, 2, 3, 4];var newThings = [];for (var i = 0; i & things. ++i) {
newThings[i] = things[i] * 10;}console.log(newThings); // [10, 20, 30, 40]
好了,现在我们没有改变 things 但从技术上讲,我们改变了 newThings 。先不去管它。毕竟是在用JavaScript。一旦迁移到一个函数式语言,我们就再也不能进行突变操作。
这部分的重点在于理解这些函数是如何工作的,并使用它们减少糟糕的代码。
把这些代码放在一个函数中。我们将使用第一个常用函数 map ,它操作旧数组中的每个值,并返回一个新数组。
var map = (f, array) =& {
var newArray = [];
for (var i = 0; i & array. ++i) {
newArray[i] = f(array[i]);
return newA};
注意函数 f ,它被传入 map 中了,所以我们可以通过 f 对数组中的每个值进行任意操作。
现在可以用 map 重写之前的代码了:
var things = [1, 2, 3, 4];var newThings = map(v =& v * 10, things);
看啊。没有 for 循环。也因此增强了可读性。
当然,从技术上讲, map 函数里也是 for 循环。但至少我们不用再一遍一遍地写模板代码了。
现在来实现另一个常用函数来从数组中过滤值:
var filter = (pred, array) =& {
var newArray = [];for (var i = 0; i & array. ++i) {
if (pred(array[i]))
newArray[newArray.length] = array[i];
return newA};
注意判断函数 pred ,返回 TRUE 则保留 item ,丢弃则返回 FALSE 。
下例展示了 filter 如何从数组中过滤出奇数:
var isOdd = x =& x % 2 !== 0;var numbers = [1, 2, 3, 4, 5];var oddNumbers = filter(isOdd, numbers);console.log(oddNumbers); // [1, 3, 5]
使用新的 filter 函数比在 for 循环中进行逻辑判断更简单。
最后一个常用函数叫 reduce 。通常来说,它用来将一个 list 缩减成一个单一值,但实际上它可以做更多事情。
在函数式语言中这个函数也叫 fold 。
var reduce = (f, start, array) =& {
for (var i = 0; i & array. ++i)
acc = f(array[i], acc); // f() takes 2 para});
reduce 函数接收一个缩减函数 f ,一个初始值 start 和一个数组。
注意缩减函数 f ,它接收两个参数,一个数组当前值,一个累加器 acc 。每次迭代 f 用这两个参数产生一个新的累加器。最终一次迭代完成后累加器被返回。
下面的例子可以帮助我们理解它如何工作:
var add = (x, y) =& x +var values = [1, 2, 3, 4, 5];var sumOfValues = reduce(add, 0, values);console.log(sumOfValues); // 15
注意 add 函数接收两个参数然后相加。我们的 reduce 期望接收一个接收两个参数的函数,它们才能很好的配合。
我们传入 0 作为起始值和传入数组的值进行加和。在 reduce 函数内部, sum 随着迭代器累加。最终的累加值返回给 sumOfValues 。
map , filter , reduce 中让我们可以对数组进行常用操作而不必重复写模板代码。
但在函数式语言中,他们更常用,因为在函数式语言中没有循环结构,只有递归。迭代器函数不只是极其有用。而且是必须的。
记在脑子里!!!!
这次先到这里。
在这个系列后面的部分,将要讨论引用完整性,执行顺序,类型等。
本文根据 @Charles Scalfani 的《 So You Want to be a Functional Programmer (Part 4) 》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处: /@cscalfani/so-you-want-to-be-a-functional-programmer-part-4-18fbe3ea9e49#.hi2v7rnd1 。
前端开发,音乐,动漫,技术控,喜欢折腾新鲜事物,以不断学习的态度,在前端的路上走下去。
最新教程周点击榜
微信扫一扫Python(7)
函数式编程
当我们说起函数式编程来说,我们会看到如下函数式编程的长相:
函数式编程的三大特性:
immutable data 不可变数据:像Clojure一样,默认上变量是不可变的,如果你要改变变量,你需要把变量copy出去修改。这样一来,可以让你的程序少很多Bug。因为,程序中的状态不好维护,在并发的时候更不好维护。(你可以试想一下如果你的程序有个复杂的状态,当以后别人改你代码的时候,是很容易出bug的,在并行中这样的问题就更多了)first class functions:这个技术可以让你的函数就像变量一样来使用。也就是说,你的函数可以像变量一样被创建,修改,并当成变量一样传递,返回或是在函数中嵌套函数。这个有点像Javascript的Prototype(参看)尾递归优化:我们知道递归的害处,那就是如果递归很深的话,stack受不了,并会导致性能大幅度下降。所以,我们使用尾递归优化技术——每次递归时都会重用stack,这样一来能够提升性能,当然,这需要语言或编译器的支持。Python就不支持。
函数式编程的几个技术
map & reduce :这个技术不用多说了,函数式编程最常见的技术就是对一个集合做Map和Reduce操作。这比起过程式的语言来说,在代码上要更容易阅读。(传统过程式的语言需要使用for/while循环,然后在各种变量中把数据倒过来倒过去的)这个很像C++中的STL中的foreach,find_if,count_if之流的函数的玩法。pipeline:这个技术的意思是,把函数实例成一个一个的action,然后,把一组action放到一个数组或是列表中,然后把数据传给这个action list,数据就像一个pipeline一样顺序地被各个函数所操作,最终得到我们想要的结果。recursing 递归 :递归最大的好处就简化代码,他可以把一个复杂的问题用很简单的代码描述出来。注意:递归的精髓是描述问题,而这正是函数式编程的精髓。currying:把一个函数的多个参数分解成多个函数, 然后把函数多层封装起来,每层函数都返回一个函数去接收下一个参数这样,可以简化函数的多个参数。在C++中,这个很像STL中的bind_1st或是bind2nd。higher order function 高阶函数:所谓高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数。现象上就是函数传进传出,就像面向对象对象满天飞一样。
还有函数式的一些好处
parallelization 并行:所谓并行的意思就是在并行环境下,各个线程之间不需要同步或互斥。lazy evaluation 惰性求值:这个需要编译器的支持。表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值,也就是说,语句如x:=&(把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到&x&中,但是先不管实际在&x&中的是什么,直到通过后面的表达式中到&x&的引用而有了对它的值的需求的时候,而后面表达式自身的求值也可以被延迟,最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。determinism 确定性:所谓确定性的意思就是像数学那样&f(x) = y ,这个函数无论在什么场景下,都会得到同样的结果,这个我们称之为函数的确定性。而不是像程序中的很多函数那样,同一个参数,却会在不同的场景下计算出不同的结果。所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化。
上面的那些东西太抽象了,还是让我们来循序渐近地看一些例子吧。
我们先用一个最简单的例子来说明一下什么是函数式编程。
先看一个非函数式的例子:
increment(){
&&&&cnt++;
那么,函数式的应该怎么写呢?
increment(int
&&&&return
cnt+1;
你可能会觉得这个例子太普通了。是的,这个例子就是函数式编程的准则:不依赖于外部的数据,而且也不改变外部数据的值,而是返回一个新的值给你。
我们再来看一个简单例子:
&&&&&&&&return
&&&&return
我们可以看到上面那个例子inc()函数返回了另一个函数incx(),于是我们可以用inc()函数来构造各种版本的inc函数,比如:inc2()和inc5()。这个技术其实就是上面所说的Currying技术。从这个技术上,你可能体会到函数式编程的理念:把函数当成变量来用,关注于描述问题而不是怎么实现,这样可以让代码更易读。
Map & Reduce
在函数式编程中,我们不应该用循环迭代的方式,我们应该用更为高级的方法,如下所示的Python代码
= map(len, [&hao&,
&coolshell&])
你可以看到这样的代码很易读,因为,这样的代码是在描述要干什么,而不是怎么干。
我们再来看一个Python代码的例子:
toUpper(item):
&&&&&&return
item.upper()
upper_name
= map(toUpper, [&hao&,
&coolshell&])
upper_name
顺便说一下,上面的例子个是不是和我们的STL的transform有些像?
#include &iostream&
#include &algorithm&
#include &string&
&&string s=&hello&;
&&transform(s.begin(), s.end(), back_inserter(out), ::toupper);
&&cout && out &&
在上面Python的那个例子中我们可以看到,我们写义了一个函数toUpper,这个函数没有改变传进来的值,只是把传进来的值做个简单的操作,然后返回。然后,我们把其用在map函数中,就可以很清楚地描述出我们想要干什么。而不会去理解一个在循环中的怎么实现的代码,最终在读了很多循环的逻辑后才发现原来是这个或那个意思。 下面,我们看看描述实现方法的过程式编程是怎么玩的(看上去是不是不如函数式的清晰?):
upname =['HAO',
'COOLSHELL']
lowname =[]
i in range(len(upname)):
&&&&lowname.append( upname[i].lower() )
对于map我们别忘了lambda表达式:你可以简单地理解为这是一个inline的匿名函数。下面的lambda表达式相当于:def func(x): return x*x
map(lambda
x, range(9))
我们再来看看reduce怎么玩?(下面的lambda表达式中有两个参数,也就是说每次从列表中取两个值,计算结果后把这个值再放回去,下面的表达式相当于:((((1+2)+3)+4)+5) )
reduce(lambda
x, y: x+y, [1,
Python中的除了map和reduce外,还有一些别的如filter, find, all, any的函数做辅助(其它函数式的语言也有),可以让你的代码更简洁,更易读。 我们再来看一个比较复杂的例子:
计算数组中正数的平均值
positive_num_cnt
positive_num_sum
i in range(len(num)):
num[i] & 0:
&&&&&&&&positive_num_cnt
&&&&&&&&positive_num_sum
positive_num_cnt & 0:
&&&&average
= positive_num_sum
/ positive_num_cnt
如果用函数式编程,这个例子可以写成这样:
positive_num
= filter(lambda
x: x&0, num)
reduce(lambda
x,y: x+y, positive_num)
/ len( positive_num )
C++11玩的法:
#include &iostream&
#include &algorithm&
#include &numeric&
#include &string&
#include &vector&
vector num {2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8};
copy_if(num.begin(), num.end(), back_inserter(p_num), [](int
i){ return
(i&0);} );
average = accumulate(p_num.begin(), p_num.end(), 0) / p_num.size();
cout && &averge: &
&& average &&
我们可以看到,函数式编程有如下好处:
1)代码更简单了。
2)数据集,操作,返回值都放到了一起。
3)你在读代码的时候,没有了循环体,于是就可以少了些临时变量,以及变量倒来倒去逻辑。
4)你的代码变成了在描述你要干什么,而不是怎么去干。
最后,我们来看一下Map/Reduce这样的函数是怎么来实现的(下面是Javascript代码)
map = function
(mappingFunction, list) {
result = [];
&&forEach(list,
&&&&result.push(mappingFunction(item));
下面是reduce函数的javascript实现(谢谢
修正的我原来的简单版本)
reduce函数
reduce(actionFunction, list, initial){
&&&&if(initial){
&&&&&&&&accumulate =
&&&&&&&&accumulate = list.shfit();
&&&&temp = list.shift();
&&&&while(temp){
&&&&&&&&accumulate = actionFunction(accumulate,temp);
&&&&&&&&temp = list.shift();
&&&&return
Declarative Programming vs Imperative Programming
前面提到过多次的函数式编程关注的是:describe what to do, rather than how to do it. 于是,我们把以前的过程式的编程范式叫做
– 指令式编程,而把函数式的这种范式叫做
– 声明式编程。
下面我们看一下相关的示例(本示例来自 )。
比如,我们有3辆车比赛,简单起见,我们分别给这3辆车有70%的概率可以往前走一步,一共有5次机会,我们打出每一次这3辆车的前行状态。
对于Imperative Programming来说,代码如下(Python):
random import
car_positions
range(len(car_positions)):
&&&&&&&&if
random() & 0.3:
&&&&&&&&&&&&car_positions[i]
&&&&&&&&print
car_positions[i]
我们可以把这个两重循环变成一些函数模块,这样有利于我们更容易地阅读代码:
random import
move_cars():
enumerate(car_positions):
&&&&&&&&if
random() & 0.3:
&&&&&&&&&&&&car_positions[i]
draw_car(car_position):
car_position
run_step_of_race():
&&&&global
&&&&move_cars()
car_position in
car_positions:
&&&&&&&&draw_car(car_position)
car_positions
&&&&run_step_of_race()
&&&&draw()
上面的代码,我们可以从主循环开始,我们可以很清楚地看到程序的主干,因为我们把程序的逻辑分成了几个函数,这样一来,我们的代码逻辑也会变得几个小碎片,于是我们读代码时要考虑的上下文就少了很多,阅读代码也会更容易。不像第一个示例,如果没有注释和说明,你还是需要花些时间理解一下。而把代码逻辑封装成了函数后,我们就相当于给每个相对独立的程序逻辑取了个名字,于是代码成了自解释的。
但是,你会发现,封装成函数后,这些函数都会依赖于共享的变量来同步其状态。于是,我们在读代码的过程时,每当我们进入到函数里,一量读到访问了一个外部的变量,我们马上要去查看这个变量的上下文,然后还要在大脑里推演这个变量的状态, 我们才知道程序的真正逻辑。也就是说,这些函数间必需知道其它函数是怎么修改它们之间的共享变量的,所以,这些函数是有状态的。
我们知道,有状态并不是一件很好的事情,无论是对代码重用,还是对代码的并行来说,都是有副作用的。因此,我们要想个方法把这些状态搞掉,于是出现了我们的 Functional Programming 的编程范式。下面,我们来看看函数式的方式应该怎么写?
random import
move_cars(car_positions):
&&&&return
map(lambda
x: x +
1 if random() &
&&&&&&&&&&&&&&&car_positions)
output_car(car_position):
&&&&return
car_position
run_step_of_race(state):
&&&&return
{'time': state['time']
&&&&&&&&&&&&'car_positions': move_cars(state['car_positions'])}
draw(state):
'\n'.join(map(output_car, state['car_positions']))
race(state):
&&&&draw(state)
state['time']:
&&&&&&&&race(run_step_of_race(state))
race({'time':
&&&&&&'car_positions': [1,
上面的代码依然把程序的逻辑分成了函数,不过这些函数都是functional的。因为它们有三个症状:
1)它们之间没有共享的变量。
2)函数间通过参数和返回值来传递数据。
3)在函数里没有临时变量。
我们还可以看到,for循环被递归取代了(见race函数)—— 递归是函数式编程中带用到的技术,正如前面所说的,递归的本质就是描述问题是什么。
pipeline 管道借鉴于Unix Shell的管道操作——把若干个命令串起来,前面命令的输出成为后面命令的输入,如此完成一个流式计算。(注:管道绝对是一个伟大的发明,他的设哲学就是KISS – 让每个功能就做一件事,并把这件事做到极致,软件或程序的拼装会变得更为简单和直观。这个设计理念影响非常深远,包括今天的Web Service,云计算,以及大数据的流式计算等等)
比如,我们如下的shell命令:
auwwx | awk
'{print $2}' | sort
-n | xargs
如果我们抽象成函数式的语言,就像下面这样:
xargs(& echo, sort(n, awk('print $2', ps(auwwx)))& )
也可以类似下面这个样子:
for_each(result, [ps_auwwx, awk_p2, sort_n, xargs_echo])
好了,让我们来看看函数式编程的Pipeline怎么玩?
我们先来看一个如下的程序,这个程序的process()有三个步骤:
1)找出偶数。
3)转成字符串返回
process(num):
&&&&&&&&return
= 'The Number: %s'
&&&&return
num in nums:
process(num)
我们可以看到,输出的并不够完美,另外,代码阅读上如果没有注释,你也会比较晕。下面,我们来看看函数式的pipeline(第一种方式)应该怎么写?
even_filter(nums):
&&&&&&&&if
&&&&&&&&&&&&yield
multiply_by_three(nums):
&&&&&&&&yield
convert_to_string(nums):
&&&&&&&&yield
'The Number: %s'
= convert_to_string(multiply_by_three(even_filter(nums)))
num in pipeline:
我们动用了Python的关键字 yield,这个关键字主要是返回一个Generator,yield 是一个类似 return 的关键字,只是这个函数返回的是个Generator-生成器。所谓生成器的意思是,yield返回的是一个可迭代的对象,并没有真正的执行函数。也就是说,只有其返回的迭代对象被真正迭代时,yield函数才会正真的运行,运行到yield语句时就会停住,然后等下一次的迭代。(这个是个比较诡异的关键字)这就是lazy evluation。
好了,根据前面的原则——“使用Map & Reduce,不要使用循环”,那我们用比较纯朴的Map & Reduce吧。
even_filter(nums):
&&&&return
filter(lambda
x: x%2==0, nums)
multiply_by_three(nums):
&&&&return
map(lambda
x: x*3, nums)
convert_to_string(nums):
&&&&return
map(lambda
x: 'The Number: %s'
% x,& nums)
= convert_to_string(
&&&&&&&&&&&&&&&multiply_by_three(
&&&&&&&&&&&&&&&&&&&even_filter(nums)
&&&&&&&&&&&&&&&)
&&&&&&&&&&&&)
num in pipeline:
但是他们的代码需要嵌套使用函数,这个有点不爽,如果我们能像下面这个样子就好了(第二种方式)。
pipeline_func(nums, [even_filter,
&&&&&&&&&&&&&&&&&&&&&multiply_by_three,
&&&&&&&&&&&&&&&&&&&&&convert_to_string])
那么,pipeline_func 实现如下:
pipeline_func(data, fns):
&&&&return
reduce(lambda
a, x: x(a),
&&&&&&&&&&&&&&&&&&fns,
&&&&&&&&&&&&&&&&&&data)
好了,在读过这么多的程序后,你可以回头看一下这篇文章的开头对函数式编程的描述,可能你就更有感觉了。
最后,我希望这篇浅显易懂的文章能让你感受到函数式编程的思想,就像OO编程,泛型编程,过程式编程一样,我们不用太纠结是不是我们的程序就是OO,就是functional的,我们重要的品味其中的味道。
Wikipedia: Python –&
补充:评论中的大家也可以读一读。
感谢谢网友S142857 提供的shell风格的python pipeline:
Pipe(object):
__init__(self, func):
&&&&&&&&self.func
__ror__(self, other):
&&&&&&&&def
generator():
&&&&&&&&&&&&for
&&&&&&&&&&&&&&&&if
&&&&&&&&&&&&&&&&&&&&yield
self.func(obj)
&&&&&&&&return
generator()
even_filter(num):
&&&&return
0 else None
multiply_by_three(num):
&&&&return
convert_to_string(num):
&&&&return
'The Number: %s'
echo(item):
&&&&return
force(sqs):
force(nums | even_filter | multiply_by_three | convert_to_string | echo)
(全文完)
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:304693次
积分:4344
积分:4344
排名:第5605名
原创:122篇
转载:54篇
评论:44条
(3)(5)(3)(2)(5)(1)(2)(6)(4)(1)(1)(6)(8)(4)(10)(5)(2)(3)(2)(2)(3)(3)(2)(7)(2)(3)(4)(8)(3)(5)(1)(5)(3)(1)(1)(7)(6)(7)(4)(6)(1)(7)(16)(2)

我要回帖

更多关于 光伏发电逆变器作用 的文章

 

随机推荐