Java8学习必备——函数式编程思维三种基本构造单元和各类函数式语言的演示
Posted 从头开始自学java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8学习必备——函数式编程思维三种基本构造单元和各类函数式语言的演示相关的知识,希望对你有一定的参考价值。
基本构造单元——筛选——需要根据筛选条件来产生一个子集合的时候,用filter
筛选(filter)是列表的一种基本操作:根据用户定义的条件来筛选列表中的条目,并由此产生一个较小的新列表。
筛选会产生一个新的列表(或集合),其大小根据筛选条件,可能小于原列表。
基本构造单元——筛选——需要根据筛选条件来产生一个子集合的时候,用filter
——Java8版本
首先制造一个从1到目标数字的区间,然后在该区间上施加filter()
方法,剔除不是约数的数字:Java的取模运算(%)返回整数除法的余数,余数为0即表示除数是被除数的约数。
基本构造单元——筛选——需要根据筛选条件来产生一个子集合的时候,用filter
——Groovy版本
基本构造单元——映射——需要就地变换一个集合的时候,用map
映射(map)操作对原集合的每一个元素执行给定的函数,从而变换成一个新的集合。
下面通过各种函数式语言改写java编写的getFactors()
来演示映射:
基本构造单元——映射——需要就地变换一个集合的时候,用map——groovy版本
最后调用了unique()方法来消除列表中的重复项,确保完全平方数的平方根(如16的平方根4)不会在列表中出现两次。
基本构造单元——映射——需要就地变换一个集合的时候,用map——Clojure版本
如果让每个函数都合并成一行,那么一系列的函数定义就可以变成一个赋值语句的列表。Clojure的(let [])
块允许创建一系列作用于仅限于块内的赋值。首先要计算的是目标数的约数,为此需准备从1到目标数的区间(range 1 (inc num))
,其中右端点写成(inc num)
是因为Clojure的区间定义不包括右端点。接着用(filter )
方法消去不需要的集合元素。一般来说,上述语句按照Clojure的习惯写出来应该是(filter #(zero? (rem num %)) (range 1 (inc num)))
,不过既然概念上是先有区间再做筛选,那么让代码的阅读次序和思路保持一致会更好一些。Clojure的thread-last宏(->>
运算符)可以帮我们做这样的次序调整。求得了全部约数之后,就是对sum
和aliquot-sum
的赋值。函数余下部分的工作是逐条判断aliquot-sum满足哪一则条件,并返回相应的关键字(以冒号开头的符号,可以当作枚举来使用)。
折叠/化约——需要把集合分成一小块一小块来处理的时候,用reduce
或fold
第三种基本套路的函数名称最为多样,而且在几种流行语言里的实现各有微妙的区别。
foldLeft
和reduce
都是catamorphism这种范畴论的态射概念具体应用到列表操纵上面的变体,catamorphism是对列表“折叠”(fold)概念的推广。
reduce
和fold
操作在功能上大致重合,但根据具体的编程语言而有微妙的区别。两者都用一个累积量(accumulator)来“收集”集合元素。reduce
函数一般在需要为累积量设定一个初始值的时候使用,而fold
起始的时候累积量是空的。函数在操作集合的时候可以有不同的次序,这点会体现在相应的函数命名上(如foldLeft
和foldRight
)。这里提到的任何一种操作,都不会改变原集合。
“fold left”的含义是:
用一个二元函数或运算符来结合列表的首元素和累积量的初始值(如果累积量有初始值的话);
重复上一步直到列表耗尽,此时累积量的取值即为折叠运算的结果。
这个过程恰好就是对一个数字列表求和的过程:从0开始,加上第一个元素,求得的结果再加上第二个元素,就这样一直进行下去,直到列表元素全部用完。
折叠操作可理解为令每一个列表元素依次结合的一次变换,变换的结果是从整个列表累积成单独的一个值。左折叠按照从左到右的次序结合列表元素,由一个初始值开始,把元素一个接一个地累加上去,最后得到一个结果。
由于加法满足交换律,无论用foldLeft()
还是foldRight()
都将得到同样的结果。但有些运算(包括减法和除法在内)不能随便调换顺序,这时foldRight()
就会派上用场。在纯函数式语言里,左折叠和右折叠的实现并不相同。右折叠允许操作无限长度的列表,而左折叠则不允许。
折叠/化约——需要把集合分成一小块一小块来处理的时候,用reduce或fold——Java8之前的版本使用Functional Java框架
Functional Java框架专为Java 8以前版本的JDK而设计,因此不得不创造性地运用单方法接口和匿名内部类来达到目的。内建的F2
类正好具备折叠操作所需的结构,用它创建了一个方法,方法的两个参数(将被此方法折叠在一起的两个值)和返回值都为Integer
类型。
折叠/化约——需要把集合分成一小块一小块来处理的时候,用reduce
或fold
——Groovy版本
Groovy的inject
方法第一个参数是初始值,第二个参数是接受两个参数,返回一个值的闭包。
fold
或reduce
常常用在需要从一个集合处理产生另一个大小不同(通常较小但不必然)的集合或单一值的情况。
支持三种基本构造单元的语言会影响代码风格
当项目选用的语言(无论是否函数式语言)支持这些抽象的时候,代码的风格会发生明显的变化。
学习函数式编程,或者任何一种新范式都有一个很大的挑战,那就是在掌握新的构造单元之后,还要善于从问题里“发现”它们的身影,从而抓住解答的脉络。
函数式编程不会用很多抽象,但每个抽象的泛化程度都很高(特化的方面通过高阶函数注入)。
函数式编程以参数传递和函数的复合作为主要的表现手段,我们不需要掌握太多作为“不确定因素”存在的其他语言构造之间的交互规则,这一点对于我们的学习是有利的。
以上是关于Java8学习必备——函数式编程思维三种基本构造单元和各类函数式语言的演示的主要内容,如果未能解决你的问题,请参考以下文章
Java8新特性函数式编程API新时间日期处理APIOptional容器类总结
Java8新特性函数式编程API新时间日期处理APIOptional容器类总结
Java8新特性函数式编程API新时间日期处理APIOptional容器类总结