什么是函数式编程

📅 2026/7/5 3:50:01 👁️ 阅读次数 📝 编程学习
什么是函数式编程

在维基百科中,已经对函数式编程有了很详细的介绍。

那我们就来摘取一下Wiki上对Functional Programming的定义:

In computer science,functional programmingis a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data.

简单地翻译一下,也就是说函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念

接下来,我们就来剖析下函数式编程的一些特征。

3. 从并发说开来

说来惭愧,我第一个真正接触到函数式编程,要追溯到两年以前的《Erlang程序设计》,我们知道Erlang是一个支持高并发,有着强大容错性的函数式编程语言。

因为时间太久了,而且一直没有过真正地应用,所以对Erlang也只是停留在一些感性认识上。在我眼里,Erlang对高并发的支持体现在两方面,第一,Erlang对轻量级进程的支持(请注意此处进程并不等于操作系统的进程,而只是Erlang内部的一个单位单元),第二,就是变量的不变性

4. 变量的不变性

在《Erlang程序设计》一书中,对变量的不变性是这样说的,Erlang是目前唯一变量不变性的语言。具体的话我记不清了,我不知道是老爷子就是这么写的,还是译者的问题。我在给这本书写书评的时候吹毛求疵地说:

我对这句话有异议,切不说曾经的Lisp,再到如今的F#都对赋值操作另眼相看,低人一等。单说如今的Java和C#,提供的final和readonly一样可以支持变量的不变性,而这个唯一未免显得有点太孤傲了些。

让我们先来看两段程序,首先是我们常见的一种包含赋值的程序:

class Account:
def __init__(self,balance):
self.balance = balance
def desposit(self,amount):
self.balance = self.balance + amount
return self.balance
def despositTwice(self):
self.balance = self.balance * 2
return self.balance

if __name__ == '__main__':
account = Account(100)
print(account.desposit(10))
print(account.despositTwice())

这段程序本身是没有问题的,但是我们考虑这样一种情况,现在有多个进程在同时跑这一个程序,那么程序就会被先desposit 还是先 despositTwice所影响。

但是如果我们采用这样的方式:

def makeAccount(balance):
global desposit
global despositTwice
def desposit(amount):
result = balance + amount
return result
def despositTwice():
result = balance * 2
return result
def dispatch(method):
return eval(method)
return dispatch

if __name__ == '__main__':
handler = makeAccount(100)
print(handler('desposit')(10))
print(handler('despositTwice')())

这时我们就会发现,无论多少个进程在跑,因为我们本身没有赋值操作,所以都不会影响到我们的最终结果。

但是这样也像大家看到的一样,采用这样的方式没有办法保持状态。

这也就是我们在之前概念中看到的无状态性。

5. 再看函数式编程的崛起

既然已经看完了函数式编程的基本特征,那就让我们来想想数十年后函数式编程再次崛起的幕后原因。

一直以来,作为函数式编程代表的Lisp,还是Haskell,更多地都是在大学中,在实验室中应用,而很少真的应用到真实的生产环境。

先让我们再来回顾一下伟大的摩尔定律:

1、集成电路芯片上所集成的电路的数目,每隔18个月就翻一番。

2、微处理器的性能每隔18个月提高一倍,而价格下降一半。

3、用一个美元所能买到的电脑性能,每隔18个月翻两番。

一如摩尔的预测,整个信息产业就这样飞速地向前发展着,但是在近年,我们却可以发现摩尔定律逐渐地失效了,芯片上元件的尺寸是不可能无限地缩小的,这就意味着芯片上所能集成的电子元件的数量一定会在某个时刻达到一个极限。那么当技术达到这个极限时,我们又该如何适应日益增长的计算需求,电子元件厂商给出了答案,就是多核。

核并行程序设计就这样被推到了前线,而命令式编程天生的缺陷却使并行编程模型变得非常复杂,无论是信号量,还是锁的概念,都使程序员不堪其重。

就这样,函数式编程终于在数十年后,终于走出实验室,来到了真实的生产环境中,无论是冷门的Haskell,Erlang,还是Scala,F#,都是函数式编程成功的典型。

6. 函数式编程的第一型

我们知道,对象是面向对象的第一型,那么函数式编程也是一样,函数是函数式编程的第一型。

我们在函数式编程中努力用函数来表达所有的概念,完成所有的操作。

在面向对象编程中,我们把对象传来传去,那在函数式编程中,我们要做的是把函数传来传去,而这个,说成术语,我们把他叫做高阶函数

那我们就来看一个高阶函数的应用,熟悉js的同学应该对下面的代码很熟悉,让哦我们来写一个在电子电路中常用的滤波器的示例代码。

def Filt(arr,func):
result = []
for item in arr:
result.append(func(item))
return result

def MyFilter(ele):
if ele < 0 :
return 0
return ele

if __name__ == '__main__':
arr = [-5,3,5,11,-45,32]
print('%s' % (Filt(arr,MyFilter)))

哦,之前忘记了说,什么叫做高阶函数,我们给出定义:

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入
  • 输出一个函数

那么,毫无疑问上面的滤波器,就是高阶函数的一种应用。

在函数式编程中,函数是基本单位,是第一型,他几乎被用作一切,包括最简单的计算,甚至连变量都被计算所取代。在函数式编程中,变量只是一个名称,而不是一个存储单元,这是函数式编程与传统的命令式编程最典型的不同之处。

让我们看看,变量只是一个名称,在上面的代码中,我们可以这样重写主函数:

if __name__ == '__main__':
arr = [-5,3,5,11,-45,32]
func = MyFilter
print('%s' % (Filt(arr,func)))

当然,我们还可以把程序更精简一些,利用函数式编程中的利器,map,filter和reduce :

if __name__ == '__main__':
arr = [-5,3,5,11,-45,32]
print('%s' % (map(lambda x : 0 if x<0 else x ,arr)))

这样看上去是不是更赏心悦目呢?

这样我们就看到了,函数是我们编程的基本单位。

7. 函数式编程的数学本质

忘了是谁说过:一切问题,归根结底到最后都是数学问题。

编程从来都不是难事儿,无非是细心,加上一些函数类库的熟悉程度,加上经验的堆积,而真正困难的,是如何把一个实际问题,转换成一个数学模型。这也是为什么微软,Google之类的公司重视算法,这也是为什么数学建模大赛在大学计算机系如此被看重的原因。

先假设我们已经凭借我们良好的数学思维和逻辑思维建立好了数学模型,那么接下来要做的是如何把数学语言来表达成计算机能看懂的程序语言。

这里我们再看在第四节中,我们提到的赋值模型,同一个函数,同一个参数,却会在不同的场景下计算出不同的结果,这是在数学函数中完全不可能出现的情况,f(x) = y ,那么这个函数无论在什么场景下,都会得到同样的结果,这个我们称之为函数的确定性。

这也是赋值模型与数学模型的不兼容之处。而函数式编程取消了赋值模型,则使数学模型与编程模型完美地达成了统一

8. 函数式编程的抽象本质

相信每个程序员都对抽象这个概念不陌生。