函数式编程(Functional Programming)是一种编程风格,它是相对于指令式编程风格而言的,常见的面向对象编程就是指令式编程风格。
指令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取、存储指令),表达式(内存引用和算术运算)和控制语句(跳转语句)。
而函数式编程是面向数学的抽象,将计算描述为一种表达式求值。这里的函数实际就是数学中的函数,即自变量到因变量的映射。也就是说,一个函数的值仅决定于函数参数的值,不依赖其他状态。
函数式编程是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
在函数式语言当中,函数作为一等公民,可以在任何地方定义,在函数内或函数外,可以作为函数的参数或返回值,可以对函数进行组合,也可以将函数赋值给变量。严格意义上的函数式编程意味着不适用可变的变量,赋值,循环和其他命令式控制结构进行编程。
函数式编程风格带来的好处是:
- 函数式编程使用不可变对象作为变量,不会修改变量的值,而是返回一个新的值,如此这样,更容易理清头绪,使得单元测试和调试更加容易;
- 可以很自由地传递不可变对象,但对于可变对象来说,传递给其他代码之前,需要先建造一个以防万一的副本;
- 一旦不可变对象完成构造以后,就不会有线程因为并发访问而破坏对象内部状态,因为根本没有线程可以改变不可变对象的状态;
- 不可变对象让哈希表键值更安全,所以哈希表键要求必须是不可变对象,否则使用可变对象,如果对象状态发生变化,那么在哈希表中就找不到这个对象了;
具体到编程语言,Scala(静态语言)和Python(动态语言)都能比较的支持函数式编程风格,但是它们都不是纯函数式的,也就是说它们同时支持指令式风格和函数式风格。而Java基本是指令式风格,但自从Java8引入lambda表达式以后也开始部分支持函数式风格。函数式编程最典型的是诸如map, flatMap, reduce, filter等函数,它们的特点是支持某个函数作为上面这些函数的参数。
下面分别以Java、Scala和Python举例函数式编程,其中Java对函数式编程只是间接的支持(通过函数式接口),支持度比较有限,而Scala和Python就对函数式编程支持的比较好。
Java函数式编程举例:
1 package lxy.java.fp;
2
3 import java.util.*;
4 import java.util.function.*;
5
6
7 public class FPDemo {
8 //定义泛型方法,用以根据第二个参数指定的条件从第一个参数指定的集合中过滤部分元素,并返回过滤后的结果。这里的第二个参数是一个函数式接口。
9 public static <T> List <T> filter(List <T> list, Predicate <T> p) {
10 List <T> results = new ArrayList <>();
11 for (T s : list) {
12 if (p.test(s)) {
13 results.add(s);
14 }
15 }
16 return results;
17 }
18
19 public static void main(String[] args) {
20 List <String> myList = Arrays.asList("Hello", "Java", "Python", "Scala");
21
22 //通过匿名类的方式
23 List <String> results = filter(myList, new Predicate <String>() {
24 public boolean test(String t) {
25 return t.length() >= 5;
26 }
27 });
28 System.out.println("through anonymous class:");
29 System.out.println("strings with length more than 5:");
30 for (String result : results) {
31 System.out.println(result);
32 }
33
34 //行为参数化,通过匿名函数(即lambda表达式)方式,
35 System.out.println("throu