函数式编程

简介

函数式编程是一种编程思想,并且被很多当今主流的编程语言所纳入实现对应的 库和API 供开发者调用

大部分语言都支持的函数式编程三套件:Map、Reduce、Filter

同时在分布式系统课程的第一节 MapReduce 中也经由 Map 和 Reduce 函数的实现中提到了一些函数式编程的思想

核心思想

函数式编程的核心思想是描述 做什么 而不是 怎么做

在函数式编程中,函数被视为第一类对象,可以作为参数传递给其他函数,也可以从其他函数返回

例如下面这段 Java 代码

1
2
3
// Lambda 表达式作为参数传递给函数
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(number -> System.out.println(number));
1
2
3
// Lambda 表达式作为返回值
Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 输出 25

通过 Function 接口实例化了一个不同寻常意义的函数,这个函数接收输入并且输出输入的平方

JDK8 中引入了 Function 这个函数式接口,引入了函数对象,可以说是用面向对象的方式来处理函数式编程的核心

纯函数

函数式编程的其中一个核心概念是纯函数

纯函数的意思是:

  • 输出的结果只和输入参数有关
  • 函数没有任何副作用(这里的副作用是指不修改除了函数之外的任何变量

纯函数举例

1
2
3
4
5
6
7
8
9
10
public class LambdaTest {
int idx;
public int pureFunctionAddOne(int num){
return num+1;
}
public int notPureFunctionAddOne(int num){
idx += num+1; //修改了其他变量,有副作用
return idx; //输出不仅仅之和输入有关
}
}

函数式接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

static <T> Function<T, T> identity() {
return t -> t;
}
}

可以看到在 Function 接口中只有一个抽象方法,剩下的都是这种有且只有一个抽象方法的接口叫做函数式接口

这里的 @FunctionalInterface 起到了一个标注作用,针对这个注解修饰的接口,编译器会强制检查该接口是否满足函数式接口的要求:“确实有且仅有一个抽象方法”,否则将会报错。

即使不使用该注解,只要一个接口满足函数式接口的要求,那它仍然是一个函数式接口,使用起来都一样。该注解只起到标记接口,指示编译器对其进行检查的作用

其他的函数式接口

Function

最常见的以及最核心的函数式接口:java.util.function.FunctionFunction 接口表示一个接受单个参数并返回单个值的函数(方法)

在上面的例子中我们也提到过,这里再看一下:

1
2
3
// Lambda 表达式作为返回值
Function<Integer, Integer> square = x -> x * x; //接收单个参数 x 并返回单个值 x*x
System.out.println(square.apply(5)); // 输出 25

这里的 Lambda表达式就是相当于一个匿名实现类,相当于是

1
2
3
4
5
6
7
8
9
10
public class SquareFunction implements Function<Integer,Integer>{
@Override
public Integer apply(Integer x) {
return x*x;
}
public static void main(String[] args){
Function<Integer,Integer> square = new SquareFunction();
System.out.println(square.apply(5)); // 输出 25
}
}

Map

有点类似 Map Reduce 中提到的 Map 和 Reduce 函数了

JDK8 Stream API 中的 map 方是接收一个参数,并返回一个值的函数

1
stream.map((value) -> value.toUpperCase())

实际应用

缓存查询

实现一个通用的缓存模板。查询一个值,先找缓存,找到则返回,没找到通过接口获取,结果保存到缓存后返回


函数式编程
http://example.com/2024/05/12/函数式编程/
作者
Noctis64
发布于
2024年5月12日
许可协议