函数式编程简介

本文深入探讨编程范式,包括命令式编程与函数式编程的区别,函数式编程的特点如第一等公民、表达式、无副作用、引用透明等,以及其意义如代码简洁、易于理解和并发编程。同时介绍了函数式编程的特性,如高阶函数、柯里化、惰性求值、闭包和计算续体。
摘要由CSDN通过智能技术生成

一、编程范式

"编程范式"programming paradigm),也就是如何编写程序的方法论

1.命令式编程:面向计算机硬件的抽象,变量(对应着存储单元)、赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),命令式程序就是一个冯诺依曼机指令序列。面向对象编程就是一种命令式编程。

2.函数式编程:面向数学的抽象,将计算描述为一种表达式的值。函数式程序就是一个表达式。

函数式编程与命令式编程的最大不同:describe what to do, rather than how to do it函数式编程关心数据的映射,命令式编程关心解决问题的步骤

例子:计算(1 + 2) * 3 – 4

命令式:

var a = 1 + 2;

var b = a * 3;

var c = b - 4;

函数式:

add(1,2).multiply(3).subtract(4)

另一个例子:二叉树镜像反转

                   

它的含义是:首先判断节点是否为空;然后翻转左树;然后翻转右树;最后左右互换。所谓“翻转二叉树”,可以看做是要得到一颗和原来二叉树对称的新二叉树。这颗新二叉树的特点是每一个节点都递归地和原树相反。这段代码体现的思维,就是旧树到新树的映射——对一颗二叉树而言,它的镜像树就是左右节点递归镜像的树。

二、特点

1.函数"第一等公民"。所谓"第一等公民"first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值

2.只用"表达式",不用"语句“。"表达式"expression)是一个单纯的运算过程,总是有返回值;"语句"statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。

3.没有"副作用"(No Side Effect)。所谓"副作用"side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。函数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。

4.不修改状态。函数式编程只是返回新的值,不修改系统变量。因此,不修改变量,也是它的一个重要特点

5.引用透明(Referential transparency)。指的是函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。

注:变量的值是不可变的(immutable),也就是说不允许像命令式编程语言中那样多次给一个变量赋值。比如说在命令式编程语言我们写“x = x + 1”,这依赖可变状态的事实,拿给程序员看说是对的,但拿给数学家看,却被认为这个等式为假

三、意义

1.代码简洁,开发快速。函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。Paul Graham黑客与画家一书中写道:同样功能的程序,极端情况下,Lisp代码的长度可能是C代码的二十分之一。

2.接近自然语言,易于理解。add(1,2).multiply(3).subtract(4)

3.更方便的代码管理。函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合

4.易于“并发编程”。函数式编程不需要考虑"死锁"deadlock),因为它不修改变量,所以根本不存在""线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"concurrency)。

5.代码的热升级。函数式编程没有副作用,只要保

证接口不变,内部实现是外部无关的。所以,可以在运行状态下直接升级代码,不需要重启,也不需要停机。Erlang语言早就证明了这一点,它是瑞典爱立信公司为了管理电话系统而开发的,电话系统的升级当然是不能停机的。

四、特性

高阶函数(Higher-order function)。高阶函数就是参数为函数或返回值为函数的函数,有了高阶函数,就可以将复用的粒度降低到函数级别。例子:

def sumInts(a: Int, b: Int): Int =
  if (a > b) 0 else a + sumInts(a + 1, b)

def sumCubes(a: Int, b: Int): Int =
  if (a > b) 0 else cube(a) + sumCubes(a + 1, b)

def sumFactorials(a: Int, b: Int): Int =
  if (a > b) 0 else fact(a) + sumFactorials(a + 1, b)

分别是求a到b之间整数之和,求a到b之间整数的立方和,求a到b之间整数的阶乘和。

我们可以定义一个高阶函数sum:
def sum(f: Int => Int, a: Int, b: Int): Int =
  if (a > b) 0
  else f(a) + sum(f, a + 1, b)
其中参数f是一个函数,在函数中调用f函数进行计算,并进行求和。

然后就可以写如下的函数
def sumInts(a: Int, b: Int) = sum(id, a, b)
def sumCubs(a: Int, b: Int) = sum(cube, a, b)
def sumFactorials(a: Int, b: Int) = sum(fact, a, b)

def id(x: Int): Int = x
def cube(x: Int): Int = x * x * x
def fact(x: Int): Int = if (x == 0) 1 else fact(x - 1)

柯里化(Currying)。是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术(函数式语言的表达能力很强。用这种语言编程的时候基本不需要设计模式),一种可以快速且简单的实现函数封装的捷径

以适配器模式为例,在设计模式中,适配器模式(英语:adapter pattern)有时候也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类能在一起工作,做法是将类自己的接口包裹在一个已存在的类中。

命令式:
int pow(int i, int j);
int square(int i)
{
    return pow(i, 2);
}
函数式:
square = int pow(int i, 2);

惰性求值(Lazy Evaluation)。是在将表达式赋值给变量(或称作绑定)时并不计算表达式的值,而在变量第一次被使用时才进行计算。这样就可以通过避免不必要的求值提升性能。

scala> val x = { println("x"); 15 }
x
x: Int = 15

scala> lazy val y = { println("y"); 13 }
y: Int = <lazy>

scala> y
y
res3: Int = 13

scala> y
res4: Int = 13

闭包(Closure)。所谓“闭包”,就是在构造函数体内定义另外的函数作为目标对象的方法函数,而这个对象的方法函数反过来引用外层函数体中的临时变量。当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

Function makeIncrementer() {
   int n = 0;

   int increment() {
       return ++n;
   }
}

Function inc1 = makeIncrementer();
Function inc2 = makeIncrementer();

inc1(); // returns 1;
inc1(); // returns 2;
inc1(); // returns 3;
inc2(); // returns 1;
inc2(); // returns 2;
inc2(); // returns 3;


def makeIncrementer():Int={ var n = 0 def increment()={++n}}

计算续体(Continuation

我们对函数的理解只有一半是正确的,因为这样的理解基于一个错误的假设:函数一定要把其返回值返回给调用者。按照这样的理解,continuation就是更加广义的函数。这里的函数不一定要把返回值传回给调用者,相反,它可以把返回值传给程序中的任意代码。continuation就是一种特别的参数,把这种参数传到函数中,函数就能够根据continuation将返回值传递到程序中的某段代码中。应用于Pipeline管道

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值