诞
生50多年之后,函
数式编程(functional programming)开始获得越来越多的关注。
不
仅最古老的函数式语言Lisp重获青春,而且新的函数式语言层出不穷,比如Erlang、clojure、Scala、F#等等。目前最当红的 Python、Ruby、Javascript,对函数式编程的支持都很强,就连老牌的面向对象的Java、面向过程的PHP,都忙不迭地加入对匿名函数
的支持。越来越多的迹象表明,函数式编程已经不再是学术界的最爱,开始大踏步地在业界投入实用。
也
许继"面向对象编程"之后,"函数式编程"会成为下一个编程的主流范式(paradigm)。未来的程序员恐怕或多或少都必须懂一点。
但
是,"函数式编程"看上去比较难,缺乏通俗的入门教程,各种介绍文章都充斥着数学符号和专用术语,让人读了如坠云雾。就连最基本的问题"什么是函数式编 程",网上都搜不到易懂的回答。
下
面是我的"函数式编程"学习笔记,分享出来,与大家一起探讨。内容不涉及数学(我也不懂Lambda
Calculus),也不涉及高级特性(比如lazy
evaluation和currying),
只求尽量简单通俗地整理和表达,我现在所理解的"函数式编程"以及它的意义。
我
主要参考了Slava Akhmechet的"Functional
Programming For The Rest of Us"。
一、定义
简
单说,"函数式编程"是一种"
编程范式"(programming paradigm),也就是如何编写程序的方法论。
它
属于"
结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。举例来说,现在有这样一个数学表达式:
(1 + 2) * 3 - 4
传
统的过程式编程,可能这样写:
var a = 1 + 2;
var b = a * 3;
var c = b - 4;
函
数式编程要求使用函数,我们可以把运算过程定
义为不同的函数,然后写成下面这样:
var result = subtract(multiply(add(1,2), 3), 4);
这
就是函数式编程。
二、特点
函
数式编程具有五个鲜明的特点。
1. 函数是"第一等公民"
所
谓"
第一等公民"(first
class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
举
例来说,下面代码中的print变量就是一个函数,可以作为另一个函数的参数。
var print = function(i){ console.log(i);};
[1,2,3].forEach(print);
2. 只用"表达式",不用"语句"
"
表达式"(expression)是一个单纯的运算过程,总是有返回值;"语句"(statement)是执行某种操作,没有返回值。函数式编程要求,只 使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。
原
因是函数式编程的开发动机,一开始就是为了处理运算(computation),不考虑系统的读写(I/O)。"语句"属于对系统的读写操作,所以就被排 斥在外。
当
然,实际应用中,不做I/O是不可能的。因此,编程过程中,函数式编程只要求把I/O限制到最小,不要有不必要的读写行为,保持计算过程的单纯性。
3. 没有"副作用"
所
谓"
副作用"(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
函
数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
4. 不修改状态
上
一点已经提到,函数式编程只是返回新的值,不修改系统变量。因此,不修改变量,也是它的一个重要特点。
在
其他类型的语言中,变量往往用来保存"状态"(state)。不修改变量,意味着状态不能保存在变量中。函数式编程使用参数保存状态,最好的例子就是递 归。下面的代码是一个将字符串逆序排列的函数,它演示了不同的参数如何决定了运算所处的"状态"。
function reverse(string) {
if(string.length == 0) {
return string;
} else {
return reverse(string.substring(1, string.length)) + string.substring(0, 1);
}
}
由
于使用了递归,函数式语言的运行速度比较慢,这是它长期不能在业界推广的主要原因。
5. 引用透明
引
用透明(Referential transparency),指的是函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。
有
了前面的第三点和第四点,这点是很显然的。其他类型的语言,函数的返回值往往与系统状态有关,不同的状态之下,返回值是不一样的。这就叫"引用不透明", 很不利于观察和理解程序的行为。
三、意义
函
数式编程到底有什么好处,为什么会变得越来越流行?
1. 代码简洁,开发快速
函
数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。
Paul
Graham在《黑客与画家》一
书中写
道:同样功能的程序,极端情况下,Lisp代码的长度可能是C代码的二十分之一。
如
果程序员每天所写的代码行数基本相同,这就意味着,"C语言需要一年时间完成开发某个功能,Lisp语言只需要不到三星期。反过来说,如果某个新功 能,Lisp语言完成开发需要三个月,C语言需要写五年。"当然,这样的对比故意夸大了差异,但是"在一个高度竞争的市场中,即使开发速度只相差两三倍,
也足以使得你永远处在落后的位置。"
2. 接近自然语言,易于理解
函
数式编程的自由度很高,可以写出很接近自然语言的代码。
前
文曾经将表达式(1 + 2) * 3 - 4,写成函数式语言:
subtract(multiply(add(1,2), 3), 4)
对
它进行变形,不难得到另一种写法:
add(1,2).multiply(3).subtract(4)
这
基本就是自然语言的表达了。再看下面的代码,大家应该一眼就能明白它的意思吧:
merge([1,2],[3,4]).sort().search("2")
因
此,函数式编程的代码更容易理解。
3. 更方便的代码管理
函
数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。
4. 易于"并发编程"
函
数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以 可以很放心地把工作分摊到多个线程,部署"并发编程"(concurrency)。
请
看下面的代码:
var s1 = Op1();
var s2 = Op2();
var s3 = concat(s1, s2);
由
于s1和s2互不干扰,不会修改变量,谁先执行是无所谓的,所以可以放心地增加线程,把它们分配在两个线程上完成。其他类型的语言就做不到这一点,因为 s1可能会修改系统状态,而s2可能会用到这些状态,所以必须保证s2在s1之后运行,自然也就不能部署到其他线程上了。
多
核CPU是将来的潮流,所以函数式编程的这个特性非常重要。
5. 代码的热升级
函
数式编程没有副作用,只要保证接口不变,内部实现是外部无关的。所以,可以在运行状态下直接升级代码,不需要重启,也不需要停机。Erlang语
言早就证明了这一点,它是瑞典爱立信公司为了管理电话系统而开发的,电话系统的升级当然是不能停机的。
相关推荐
标题中的“函数式编程初探共2页.pdf.zip”表明这是一个关于函数式编程的压缩文件,其中包含一个两页的PDF文档。函数式编程是一种编程范式,它将计算视为数学函数的求值,强调避免可变状态和副作用,使得程序更加简洁...
图形计算器,作为现代教育中辅助学生理解数学概念的工具之一,不仅在函数图像绘制、统计数据分析等基础数学领域有着广泛的应用,而且其提供的编程功能也为学生提供了探索数学深层次奥秘的新途径。本文将深入探讨图形...
【初探Delphi模式编程】 Delphi是一种基于Object Pascal的集成开发环境,它以其高效、易用和强大的组件化编程闻名。模式编程是软件工程中的一种最佳实践,它通过使用预定义的解决方案模板来解决常见设计问题。在...
### 初探uCOS-II:嵌入式操作系统的基础与应用 #### 一、uCOS-II简介 uCOS-II,全称MicroC/OS-II,是一款专门为嵌入式系统设计的操作系统内核。它由Jean J. Labrosse创建,并在开源社区中得到了广泛的认可和支持。...
【初探传说中的setImmediate函数】这篇文章主要探讨了JavaScript中处理单线程执行阻塞问题的方法,特别是如何利用异步调用来改善用户体验。JavaScript的单线程特性意味着长时间运行的操作会阻塞UI更新,影响交互性。...
- Kotlin 支持函数式编程风格,包括 Lambda 表达式、高阶函数等。 - Lambda 表达式的使用可以使代码更加简洁,例如,设置点击监听器的代码如下所示: ```kotlin findViewById(R.id.content).setOnClickListener ...
函数式编程也是Python的一大亮点。高阶函数、闭包、装饰器等概念可以帮助学生开发出更高效、可读性更强的代码。教师可以通过讲解和实践案例,使学生熟悉这些高级特性。 此外,Python在数据处理和分析方面有广泛的...
其实在函数式编程中,函数里面还可以嵌套函数,如下面这样 def foo(): print(hello world in foo) def bar(): print(hello world in bar) 此时我们调用foo函数,执行结果会是什么样子的呢?? hello world in ...
2. 函数式编程元素:支持闭包和块,可以实现简洁的代码结构。 3. 强大的元编程能力:Ruby允许在运行时修改类和对象,增强了灵活性。 **二、Rails框架** 1. MVC架构:Rails将应用程序分为模型(Model)、视图(View...
1. 案例的多样性:确保案例覆盖不同的编程范式和技术,如面向对象编程、函数式编程等,以拓宽学生的视野。 2. 持续评估和反馈:定期检查学生的学习进度,给予及时的反馈,以便调整教学策略,确保学生跟上学习节奏。 ...
8. 数据结构的发展趋势:文章中也可能会提及数据结构未来的发展方向,比如面向对象的数据结构、函数式编程中的数据结构设计等。 由于提供的内容片段信息不完整,无法直接展示文章中的具体内容,但通过以上的知识点...
这包括面向对象编程(OOP)、函数式编程(FP)等,以及设计模式的应用。设计模式是对常见问题的解决方案,如单例模式、工厂模式等,它们可以帮助程序员更高效地解决实际问题。通过深入理解这些编程范式,可以提高...
"初中Python程序设计教学方法初探"的主题,旨在探讨如何有效地将Python编程引入中学教育,帮助学生建立起编程思维,培养他们的逻辑分析能力和创新能力。 Python教学在初中阶段的实施,首先需要关注的是教学内容的...
前言 本文受启发于Trisha Gee在JavaOne 2016的主题演讲Refactoring to ...不要怕,具体到语言层面上Lambda表达式不过是一种新的语法而已,有了它,Java将开启函数式编程的大门。 为什么需要Lambda表达式 不
关键字是编程的基础,运算符的理解需结合数学知识,而常量库函数如`scanf()`和`printf()`则是初学者必须掌握的输入输出函数。 - 程序基本结构的学习不容忽视。良好的编程习惯始于理解程序的结构,如顺序结构、选择...
2.1.2 Scala:Scala是一种多范式编程语言,它集成了面向对象和函数式编程的特点。虽然使用Scala开发Android应用相对较少,但它提供了强大的语言特性和表达能力。 2.1.3 Java:Java是Android开发最常用的语言,...
【中职Python语言教学方法初探】 在职业教育中,Python语言的教学显得尤为重要,尤其是在计算机及网络信息安全等相关专业中。Python以其简洁的语法和强大的功能,成为初学者学习编程的首选语言。然而,如何提高中职...
这篇"ASP初探(为了忘却而去纪念)"很可能是为那些想要入门ASP编程的新手准备的教程,旨在提供一个基础的、详细的指南。 在ASP中,开发者可以使用VBScript或JScript等脚本语言编写代码,这些代码在服务器上执行后...
教学内容应紧密围绕计算机等级考试的大纲,确保学生掌握考试所要求的基本语法、数据结构、函数、模块化编程、异常处理等关键知识点。同时,还要涉及面向对象编程、文件操作、网络编程等进阶主题,以应对可能出现的...