什么是函数式编程

Posted liyutian

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是函数式编程相关的知识,希望对你有一定的参考价值。

导读

建议先阅读一下这几篇博客:
函数式编程初探
函数式编程入门教程
图解 Monad

什么是函数式编程

函数式编程中的函数指的并不是编程语言中的函数(或方法),它指的是数学意义上的函数,即映射关系(如:y = f(x)),就是 y 和 x 的对应关系。

数学上对于函数的定义是这样的:“给定一个数集 A,假设其中的元素为 x。现对 A 中的元素 x 施加对应法则 f,记作 f(x),得到另一数集 B。假设 B 中的元素为 y。”

所以当我们在讨论“函数式”时,我们其实是在说“像数学函数那样,接收一个或多个输入,生成一个或多个结果,并且没有副作用(稍后解释)”
技术分享图片

实际上当一个函数无论在何处、何时调用,如果使用相同的输入总能持续地得到相同的结果,我们就可以说他具备了函数式的特征。

需要注意的是,纯函数式编程语言中的变量指的也不是命令式编程语言中的变量(即储存状态的单元),而是代数的变量,表示一个未知的数,或者一个可带入函数的值。
(在代数中对于“数本身是什么”这样的问题并不关心,它关心是各种关系及其性质)

所以函数式编程语言中所说的变量只是一个变量的名称,变量的值是不可变的(immutable),也就是说不允许像命令式编程语言中那样多次给一个变量赋值。
(PS:我们真的需要变量吗?

函数是语言中的变量:

  • 变量的值一旦绑定就不再发生变化
  • 变量中存放的与其说是指,不如说是表达式(惰性求值)

函数式编程语言的特性

在了解了函数编程语言的基本概念之后,我们再来看一下函数式编程语言所具备一些特性,理解这些特性将有助于我们更好的理解“什么是函数式编程”。

一,函数是一等公民

函数是一等公民,它的意思就是函数与其他数据类型一样,可以把它们存在数组里,当做参数传递,赋值给变量,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,也可以对函数进行组合。

二,高阶函数

在函数式编程中,如果函数能满足下面任一要求就可以被称为高阶函数(higher-order function):

  • 接受至少一个函数作为参数
  • 返回的结果是一个函数

三,柯里化

所谓“柯里化” ,就是把一个多参数的函数 f,转换为单参数函数 g,并且这个函数的返回值也是一个函数。

// 柯里化之前
function add(x, y) {
  return x + y;
}
add(1, 2) // 3
// 柯里化之后
function addX(y) {
  return function (x) {
    return x + y;
  };
}
addX(2)(1) // 3

四,副作用

所谓“副作用”,指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
在像 C++ 这样的命令式语言中,函数的意义与数学函数完全不同。例如,假设我们有一个 C++ 函数,它接受一个浮点参数并返回一个浮点结果。从表面上看它可能看起来有点像数学函数意义上的映射实数成实数,但是 C++ 函数可以做的不仅仅是返回一个取决于其参数的数字,它还可以读写其他的全局变量,也可将将输出写入屏幕并接收来自用户的输入。但是,在纯函数式语言中,函数只能读取其参数提供给它的内容,并且它对世界产生影响的唯一方式就是通过它返回的值。

五,纯函数

纯函数编程和函数编程的区别在于:是否允许在函数内部执行一些非函数式的操作,同时这些操作是否会暴露给系统中的其他地方?也就是是否存在副作用。如果不存在副作用,或者说可以不用在意这些副作用,那么就将其称为纯粹的函数式编程。

六、引用透明性

函数无论在何处、何时调用,如果使用相同的输入总能持续地得到相同的结果,就具备了函数式的特征。这种不依赖外部变量或“状态”,只依赖输入的参数的特性就被称为引用透明性(referential transparency)。
“没有可感知的副作用”(比如不改变对调用者可见的变量,进行I/O,不抛出异常等)的这些限制都隐含着引用透明性

五,递归和迭代

对于函数式而言,循环体有一个无法避免的副作用,就是它会修改某些对象的状态,通常这些对象又是和其他部分共享的。而且也因为变量值是不可变的,纯函数编程语言也无法实现循环。
所以纯函数编程语言通常不包含像 while 和 for 这样的迭代构造器,而是采用的无需修改的递归。
下面是一段标准的基于循环的方式来计算阶乘:

static int factorialIterative(int n){
    int r = 1;
    for (int i = 1; i <= n; i++) {
        r *= 1;
    }
    return r;
}

而采用递归形式的方式如下:

static long factorialRecursive(long n){
    return n == 1 ? 1 : n * factorialRecursive(n-1)
}

扩展阅读
http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html
https://www.zhihu.com/question/28292740
https://blog.csdn.net/qq_31179577/article/details/78745248














以上是关于什么是函数式编程的主要内容,如果未能解决你的问题,请参考以下文章

《On Java 8》中文版 第十三章 函数式编程

web代码片段

什么是函数式编程?

编程范式:函数式编程

Java函数式编程和lambda表达式

Java函数式编程和lambda表达式