版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Python良好编程习惯如果进行的是简单的处理,请使用字符串方法,这样可避免复杂的正则表达式所带来的错误,并保证程序的可读性。在原始字符串末尾使用反斜杠是语法错误。正则表达式的模式字符串中常常包含反斜杠字符。使用原始字符串来创建模式,可以避免对其中每个反斜杠进行转义的必要,使模式字符串更容易理解。[说明:原始字符串(RawString),也就是在字符串之前加上字符前缀r后创建的一个字符串。通常,如果在字符串中出现\,Python会把它视为转义字符,并试图用正确的转义序列替换元字符\及其后续字符。但是,如果元字符\出现在一个原始字符串中,Python不会把它解释成转义字符,而是将其视为字面意义的反斜杠字符。例如,Python会将〃\n〃解释成换行符,但将r"\n〃解释成两个字符一-一个反斜杠和一个n。]用函数式编程语言(比如说Scheme)写的程序可以反映出数学表达式的结构;数学表达式是由若干函数字符串组成的,其中的每一个都能计算出一个结果并且不产生任何副作用。同样的函数用同样的参数去调用就会产生同样的结果,这和调用时的上下文是无关的。用这样的方法可以将我们的代码优雅地结构化(并且一定程度上也起到了简化的效果)。Python编程语言拥有所有让它在函数式编程(FP)上有优势的特性。在这篇文章中,我们将从“Pythonic”的角度来看一下FP中的一些有趣的思想如高阶函数、闭包、lambda以及柯里什么是函数式编程?作为程序的一部分,我们所写的函数只是在表面上和数学函数类似。比方说我们写这么一个函数:intcurrent_balance=100;intwithdraw(intw){current_balance=current_balance—w;
returncurrent_balance;}我们调用一次“withdraw(lO)”之后90这个值将会被返回。再调用一次“withdraw(lO)”则返回80。如果“withdraw”是一个“纯”数学函数的话,那么两次调用应该返回同样的值,因为我们传给它的参数值是一样的。而我们的程序则“记住”了前次的调用(它有“状态”)然后每次都返回一个新的值。一个如下的数学方程:y=f(a)+g(b)+f(a)有一个很好的属性就是它可以被化简为:y=2*f(a)+g(b)事实上如果有一个计算机程序是由一组会修改全面变量的函数构成的话,我们是无法对它进行这样化简的。相比于生成一个数学证明,验证一个计算机程序的正确性更像是一个探索各种假设分析的情境的过程。“赋值”操作符也带来了些问题。让我们简单地看一个计算阶乘的循环:#Computefactorialof'n'intf二1,n二5;while(n>0){f=f*n;n=n—1;}returnf;我们会犯的一个常见的错误就是在循环体内交换两个语句的顺序。因为赋值操作符改变了它左边的符号的值,所以这让我们在考虑在我们的程序中每一个动作的顺序时需要格外的小心。
函数式编程的范式鼓励我们把程序结构化成一系列“纯”的函数,这些函数没有全局的状态也不使用赋值操作符(注意这并不是所有情况下都可行的,很显然一个银行的系统就需要“记住”很多东西)。函数式编程人员使用递归的函数调用(循环可以当成一种特殊的递归,于是特定的循环结构“while”和“for”也就不需要了)来产生重复性的行为。函数则成为了我们的“一等公民”,比方说,它们可以被当成参数传给另一个函数,也可以被别的函数返回,这就促成了我们称之为“高阶函数”的产生。当和“闭包”这个思想结合在一起的时候,这就成为了一个很强大的思想,它可以很简单地捕获很多复杂的可计算模式。递归表示Python中定义一个递归函数没什么了不起的。这里是一个用Python写的经典阶乘函数:deffact(n):if(n==0):return1returnn*fact(n-1)作为一等公民对象的函数让我们在Python的命令行中定义两个函数,然后来做些实验:>>>>>>defsqr(x):returnx*x>>>defcube(x):returnx*x*x>>>sqr<functionsqrat0x402ba10c>>>>a=[sqr,cube]>>>a[0](2)>>>defcompose(f,g):returnf(g(x))
>>>compose(sqr,cube,2)64>>>我们先将两个函数存到数组中,然后用“a[0][2]”的方式来调用“sqr”。接着我们又定义一个叫“compose”的函数,然后将“sqr”和“cube”这两个函数作为参数传给它们并进行调用。注意这边并没有任何特殊的记号,我们操作函数时就好像它们是像数组和数字之类的对象一样。高阶函数的威力StructureandInterpretationofComputerPrograms,这本由Abelson和Sussman写的传奇般的“指南书”中详细地描述了高阶函数的作用。一个“函数”(或子程序、辅程序、过程)被认为是一种捕获模式的机制。如果我们有如下形式的语句a*a*ab*b*bc*c*c我们可以想到要定义一个叫“cube”(立方)的函数来捕获这种模式的本质并给它一个名字。将函数当成参数传给别的函数的这种能力拓宽了这种“模式捕获”机制的领域。让我们来看一个简单的函数,叫“sum”:defsum(a,b):if(a>b):return0else:returna+sum(a+1,b)这个函数将从“a”到“b”的所有数字进行累加。我们尝试着拓宽这个函数的领域,让它具有操作任意序列的能力,比如说:
1/(1*3)+1/(5*7)+1/(9*11)+...我们发现到上面的序列和下面这个:1+2+3+4+...有着相似之处,即它们都是“求和”。我们想象一个变量“a”从1变到2,2变到3,然后一直这么做下去。这种从1到2的变化可以用一个函数来捕获(就是一个简单的“加1”函数)。在第一个序列中,这个变量是从1到5,5到9,9到11这么变化的。所以在这里的这种变化我们可以用一个“加4”的函数来捕获。另一个小问题是,这个数列中的通项并不是数字1、5、9、11等等而是1/(1*3)、1/(5*7)……但是接着这种转换也可以通过一个函数表示!我们观察的结果可以用一个函数“sigma”表示出来:defsigma(term,a,next,b):if(a>b):return0returnterm(a)+sigma(term,next(a),next,b)然后看看我们怎么通过“sigma”来计算这个序列的和:1/(1*3)+1/(5*7)+1/(9*11)+...我们来定义两个函数:defterm(x):return1.0/(x*(x+2))defnext(x):returnx+4然后调用:sigma(term,1,next,1000)这样就成功了!现在我们就能只要定义两个辅助函数就可以累积任意数列的和了。故事还没有结束。我们可以想想怎么将“sigma”进行泛化。我们注意到“sigma”只是使用了一个组合函数“add”来将一个序列中的各项“组合”起来。为什么不用一个泛化的过程
来根据一个用户定义好并作为参数传过来的函数来组合函数中的各项呢?读者可以把这个当成一个练习来试试!使用“lambda”我们再来试试下面些命令:>>>>>>lambdax:x+4<function〈lambda;〉at0x402ba25c>>>>f=lambdax:x+4>>>f(3)7>>>lambda关键字创建了一个匿名函数。lambda的主体只能由一些简单的表达式组成。在上一个例子中,我们用lambda来创建一个函数,这个函数接受一个参数,并将它加4后返回。当我们需要定义一个函数而只将这个函数用于传递给另一个函数的时候,我们应当想到使用“lambda”。比如说:>>>>>>map(lambdax:x*x,[1,3,7,9])[1,9,49,81]>>>filter(lambdax:x%2==0,range(lO))[0,2,4,6,8]>>>map函数接受一个函数和一个列表作为参数,然后将函数运用在原列表的每一个元素上,并将得到的新列表返回。filter也是类似的,它返回的列表只由原列表中那些能使函数返回true的元素组成。闭包Python允许嵌套的函数定义。defadd(x):returnlambda(y):x+y调用add(3)将返回一个带单参数的函数。现在这个返回的函数有一个特性一一它能记住它被创建时的环境。函数体中的“x”是我们在调用“add”时提供给它的。我们把这样的函数叫作“闭包”。调用add(3)(4)将使这个函数以x=3和y=4的值执行。也许你已经注意到了一些有趣的地方。我们并没有定义一个接受两个参数的“add”函数,而是可以将一个单参数的函数嵌在另一个单参数的函数中,并产生相同的效果。我们也可以将这升到任意层次:defadd3(x):returnlambday:lambdaz:x+y+z现在我们就可以调用“add3(l)(2)(3)”了!这个思想在FP社区中被称为“柯里化”。让我们试着写一个做“数值”微分的函数。defdifferentiate(f):returnlambdax:(f(x+0.001)-f(x))/0.001这个函数可以这么试出来:>>>>>>p=differentiate(cube)>>>p(2)>>>用“cube”作为参数来调用differentiate会返回一个单参数函数,这个函数会记得“f”的值等于“cube”。现在,用2作为参数来调用这个函数就会产生如下的计算:(cube(2+0.001)—cube(2))/0.001关于lambda的一点好玩的地方函数式编程中“用函数来计算”的思想可以走向极端;我们甚至可以说万事万物(是的,我的意思是甚至是整数,真值,假值这样的东西,也就是字面上的万事万物)都可以表示成函数。一个十分聪明的家伙AlonzoChurch指出如何做到这一点并产生一个了不起的成果,称之为“Lambda演算”。我们并不会深入到Church的成果的细节中,但是会来简单地看一些好玩的函数。让我们看下我们对于“true”和“false”的定义:true=lambdax,y:xfalse=lambdax,y:yiff=lambdap,x,y:p(x,y)我们定义的true为一个函数,它返回两个参数中的前一个;而false则返回后一个参数。当我们看看我们怎么使用这样的“true”和“false”的时候就会明白这种定义的逻辑了。调用iff(true,2,3)将返回2,而调用iff(false,2,3)则返回3。我们之前说的是所有可计算的建构都可以通过lambda定义出来。那么让我们来试着建立一个基本的数据结构,一个“pair”。pair=lambdax,y:lambdaf:f(x,y)这种定义有点小技巧:“pair”是一个接受两个参数的函数,并返回另一个函数;与此同时,这个返回的函数接受一个参数,并将这个参数运用在x和y上。当我们定义另两个函数的时候这种思想就变得很明了了。
first二lambdap:p(true)second=lambdap:p(false)现在,让我们变得哲学一点并问一个问题:“什么是pair?”答案是:“如果存在两个函数'first'和'second'使得first(P)为x且second(P)为y,则我们说P是这两个x和y的一个pair。”让我们这么试一下:>>>>>>P=pair(2,3
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年传染性单核细胞增多症合并淋巴结肿大治疗模拟题及答案详解
- 2026年船员考试模拟题及答案详解
- 2026年国家公务员考试行测模拟题及详细解答及答案详解
- 2026年贵阳生活模拟题及答案详解
- 2026年国家公务员考试行测常识部分模拟题及答案详解
- 2026年高级会计职称考试高级会计实务模拟题及答案详解
- 2026年公考行测模拟题常识及答案详解
- 2026年病毒性脑炎所致精神障碍诊断模拟题及答案详解
- 2026年度月嫂培训模拟题卷及答案详解
- 2026年创意作文模拟题及答案详解
- 医院咨询服务解决方案
- 高中军训军事理论课件
- bot项目建设合同范本
- 项目四流量检测仪表任务7认识靶式流量计26课件
- 质量管理五大工具培训教材
- 肩袖损伤中西医结合诊疗指南
- 血流导向装置治疗动脉瘤
- 肝脓肿术后的护理查房
- 教师系列任现职以来教学工作情况证明材料
- GB/T 19183.1-2024电气和电子设备机械结构户外机壳第1部分:设计导则
- 绿网苫盖合同范例
评论
0/150
提交评论