再识函数式编程
Posted CodeThings
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了再识函数式编程相关的知识,希望对你有一定的参考价值。
关键词:引用透明(Referential transparency),副作用,对调用者无副作用,纯函数,柯里化(currying),编程范式paradigm,声明式编程
之前陆陆续续接触和学习了函数式编程,这次是再梳理和再认识。
Java8流收集器带来的启示
我们可以选择在Java的接口里定义若干函数式接口,这样这个接口所实现的逻辑便被分割成了若干函数式接口。在抽象层,关心的是封装过程中涉及的入参的类型和转化。具体的抽象数值计算交给调用方传入,比如Java JDK里的Collectors就是使用这种思路完成的对各种流做收集的工具类
副作用与引用透明
函数的副作用是指:调用该函数的过程里,修改了任何全局数据结构或者任何传入的参数结构。
没有副作用的函数就是纯粹的函数,以kotlin为例
fun f(x:Int): Int{
return x + 1
}
函数f就是纯粹的函数(纯函数),没有副作用。
有一种特殊情况,尽管函数修改了全局变量,但是对于这个函数的调用者没有产生影响,也可以认为这个是函数式的。比如对于I/O的读写,这类输入输出结构是独立于运行环境之外的系统全局变量
fun foo(message: String){
println(message)
}
foo函数就是非纯函数
引用透明:调用纯函数这被称为引用透明
柯里化
函数式编程里,还有一个概念叫做函数柯里化。即是把一个带多参的函数转成带部分参数的函数,它返回一个新的函数,剩余的参数传入这个新的函数。用公式表达即为:f(x,y) = (g(x))(y)
柯里化能让一大块逻辑以入参的维度划分成几个子模块,增加了灵活,逻辑粒度更细腻了,能更好的应对业务场景的变动。
函数式的解决思路
如何解决数据结构修改的可见性缺陷?
函数式的解决办法是禁止使用带有副作用的方法,由于函数式方法不允许修改任何全局数据结构或者任何传入的参数,因此如果需要使用表示计算结果的数据,则创建它的一个副本而不直接修改现存的数据结构。也即在做任何改动之前都创建一份新的数据结构,只要确保按照用户的需求传递给他正确版本的数据结构就好了。
如何保存状态?使用递归
在Java里,被final修饰的对象能保证的不会有新的引用指向该对象,但是对象本身的数据结构允许改变,这是违背函数式引用透明原则的。
模式匹配
先看个Kotlin的模式匹配,不完全的pattern matching
when (x) {
in 2..20 -> print("x is in the range")
in Numbers -> print("x is valid")
else -> println("none")
}
其实Kotlin对模式匹配支持的不够好
函数式编程的优势
天然的支持并发
一旦并发和可变状态的对象揉到一起,它们引发的复杂度要远超我们的想象。而纯函数由于输出的值只依赖于输入,所以可以放心的在多个线程调用。
代码逻辑接近自然语言的逻辑,各种DSL就是通过函数式的语言特性来组织的,这样的实现得益于引用的改动,行为的参数化
参考资料
Java8实战
现代编程语言最有趣的 10 大特性
Data class 是好东西,Kotlin 就差一个 pattern matching 了
以上是关于再识函数式编程的主要内容,如果未能解决你的问题,请参考以下文章