写给大忙人看的: java和函数式编程

什么是函数式编程

此处的函数不同于OOP中的方法,可以理解成数学中的函数,是一种将输入集与输出集关联的表达式。和命令式编程不同,函数式编程只取决于它的输入,不依赖于函数外状态。

编程范式的分类: 编程范式可以分为命令式和声明式。

  1. 命令式将程序定义为一系列语句。通过语句改变程序的状态,最终达到最终的状态,面向状态编程。OOP是命令式范式的扩展。
  2. 声明式范式专注于“程序要实现什么”,而不是“程序如何实现”,面向行为编程。函数式编程是声明式编程的子集。

函数式核心概念

Haskell是纯函数式语言,入门曲线高,使用范围小。当今大多数流行语言都是支持多种编程方式的,例如java、Scala、Kotlin。以下结合java举例。

函数是一等值

如果一种编程语言将函数视为一等公民,那么它就被称为具有一等函数。这意味着函数支持其他一等值都具有的操作: 赋值给变量、作为参数传递、动态生成、作为其他函数返回值返回等。

高阶函数

将函数看做一等值,等价于语言中的存在,就很自然的想能不能写一个函数,实现一些传入函数,返回一个函数? 这就是高阶函数。

高阶函数也叫复合函数。在java中通常通过函数式接口的default方法来实现的。例如Functionjava.util.function.Function#composejava.util.function.Function#andThen。当然也可以自己实现函数的复合。

纯函数

纯函数是没有状态的、无副作用的,这和OOP冲突。面向对象鼓励我们将对象的状态隐藏,只公开访问和修改对象的必要方法。甚至,将数据存储到数据库中,对于纯函数而言也是“有副作用”的。更有甚者,连打日志都认为是一种副作用。

在实际生产中,我们要有自己的界限,不是“纯”的就一定是好的、是先进的。

此外,纯函数通常认为是不应该抛出任何异常的。

不可变性

不变性是函数式编程的核心原则之一。例如Clojure,默认上变量是不可变的,如果你要改变变量,你需要把变量copy出去修改。这对于并行程序来说,bug会大大减少。java中可以通过fianl来实现。

不可变的数据结构所有字段都是不可变的,其嵌套数据结构也要满足不可变性。除构造函数外应该没有其他的set方法。

Referential Transparency 透明引用

函数的返回值只依赖于其输入值,这种特性就称为引用透明性(referential transparency)

如果将表达式替换为其相应的值对程序的行为没有影响,那么我们称之为引用透明。这需要函数是纯粹的不可变的。它产生了一个与上下文无关的代码块,可以在任意地方、以任意顺序执行。这提供了许多优化的可能性。

尾递归优化

迭代调用发生在函数的最后。因此不需要没深入调用一层就新建一个栈。

需要编译器支持,目前java还不支持,Scala和Groovy都已经支持了。本质上就是通过重用栈的方式来优化递归的性能损耗。

Monads

一个自函子范畴上的幺半群。

这个解释太费脑了,摘自阮一峰的解释更容易理解些: Monad就是一种设计模式,表示将一个运算过程,通过函数拆解成互相连接的多个步骤。可以简单的理解成利用map、reduce、predict等方式的流式编程。

在java中,Monads可以具象成Optional、Stream、CompletableFuture等。Optional 可以包装一个值并应用一系列转换,例如使用 flatMap添加另一个包装值的转换。

Currying

在传递函数时,该函数是携带参数进行传递的。

局部套用是一种数学技巧,它将一个接受多个参数的函数转换成一个接受单个参数的函数序列。在这种技术中,可以不需要调用一个函数的所有参数。

以下举一个例子说明:

// 根据入参mass来获得一个Function,该Function输入gravity,输出mass * gravity
Function<Double, Function<Double, Double>> weight = mass -> (gravity -> mass * gravity);
// 地球上的重力函数
Double, Double> weightOnEarth = weight.apply(9.81);
logger.log(Level.INFO, "在地球上我的体重为" + weightOnEarth.apply(60.0));
// 火星上的重力函数
Function<Double, Double> weightOnMars = weight.apply(3.75);
logger.log(Level.INFO, "在火星上我的体重为: " + weightOnMars.apply(60.0));

如果不用Currying,可能在计算重力时还要再传入一个mass参数。

Currying 依赖于语言来提供两个基本特性: lambda 表达式和闭包。

和其他语言不同,java8中的闭包要求lambda表达式中的变量必须是final修饰的或实际上是final的,这是出于线程安全的考虑。

相关文章

  1. Package java.util.function
  2. Functional Programming in Java

您的点赞和评论是对我最大的认可!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值