美文网首页互联网科技
Java8学习必备知识——函数式编程思维三种基本构造单元和各类函

Java8学习必备知识——函数式编程思维三种基本构造单元和各类函

作者: 每天学点编程 | 来源:发表于2017-09-29 21:51 被阅读52次

    请关注我的微信公众号


    个人微信公众号

    技术交流群 (仅作技术交流):642646237
    请关注我的头条号:


    基本构造单元——筛选——需要根据筛选条件来产生一个子集合的时候,用filter

    筛选(filter)是列表的一种基本操作:根据用户定义的条件来筛选列表中的条目,并由此产生一个较小的新列表。

    筛选会产生一个新的列表(或集合),其大小根据筛选条件,可能小于原列表。

    基本构造单元——筛选——需要根据筛选条件来产生一个子集合的时候,用filter——Java8版本


    首先制造一个从1到目标数字的区间,然后在该区间上施加filter()方法,剔除不是约数的数字:Java的取模运算(%)返回整数除法的余数,余数为0即表示除数是被除数的约数。

    基本构造单元——筛选——需要根据筛选条件来产生一个子集合的时候,用filter——Groovy版本

    基本构造单元——映射——需要就地变换一个集合的时候,用map


    映射(map)操作对原集合的每一个元素执行给定的函数,从而变换成一个新的集合。

    基本构造单元——映射——需要就地变换一个集合的时候,用map——groovy版本

    java编写的getFactors()


    使用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宏(->>运算符)可以帮我们做这样的次序调整。求得了全部约数之后,就是对sumaliquot-sum的赋值。函数余下部分的工作是逐条判断aliquot-sum满足哪一则条件,并返回相应的关键字(以冒号开头的符号,可以当作枚举来使用)。

    折叠/化约——需要把集合分成一小块一小块来处理的时候,用reducefold

    第三种基本套路的函数名称最为多样,而且在几种流行语言里的实现各有微妙的区别。

    foldLeftreduce都是catamorphism这种范畴论的态射概念具体应用到列表操纵上面的变体,catamorphism是对列表“折叠”(fold)概念的推广。

    reducefold操作在功能上大致重合,但根据具体的编程语言而有微妙的区别。两者都用一个累积量(accumulator)来“收集”集合元素。reduce函数一般在需要为累积量设定一个初始值的时候使用,而fold起始的时候累积量是空的。函数在操作集合的时候可以有不同的次序,这点会体现在相应的函数命名上(如foldLeftfoldRight)。这里提到的任何一种操作,都不会改变原集合。

    “fold left”的含义是:
    用一个二元函数或运算符来结合列表的首元素和累积量的初始值(如果累积量有初始值的话);
    重复上一步直到列表耗尽,此时累积量的取值即为折叠运算的结果。
    这个过程恰好就是对一个数字列表求和的过程:从0开始,加上第一个元素,求得的结果再加上第二个元素,就这样一直进行下去,直到列表元素全部用完。


    折叠操作可理解为令每一个列表元素依次结合的一次变换,变换的结果是从整个列表累积成单独的一个值。左折叠按照从左到右的次序结合列表元素,由一个初始值开始,把元素一个接一个地累加上去,最后得到一个结果。

    由于加法满足交换律,无论用foldLeft()还是foldRight()都将得到同样的结果。但有些运算(包括减法和除法在内)不能随便调换顺序,这时foldRight()就会派上用场。在纯函数式语言里,左折叠和右折叠的实现并不相同。右折叠允许操作无限长度的列表,而左折叠则不允许。

    折叠/化约——需要把集合分成一小块一小块来处理的时候,用reduce或fold——Java8之前的版本使用Functional Java框架


    Functional Java框架专为Java 8以前版本的JDK而设计,因此不得不创造性地运用单方法接口和匿名内部类来达到目的。内建的F2类正好具备折叠操作所需的结构,用它创建了一个方法,方法的两个参数(将被此方法折叠在一起的两个值)和返回值都为Integer类型。

    折叠/化约——需要把集合分成一小块一小块来处理的时候,用reducefold——Groovy版本

    Groovy的inject方法第一个参数是初始值,第二个参数是接受两个参数,返回一个值的闭包。

    foldreduce常常用在需要从一个集合处理产生另一个大小不同(通常较小但不必然)的集合或单一值的情况。

    支持三种基本构造单元的语言会影响代码风格

    当项目选用的语言(无论是否函数式语言)支持这些抽象的时候,代码的风格会发生明显的变化。
    学习函数式编程,或者任何一种新范式都有一个很大的挑战,那就是在掌握新的构造单元之后,还要善于从问题里“发现”它们的身影,从而抓住解答的脉络。

    函数式编程不会用很多抽象,但每个抽象的泛化程度都很高(特化的方面通过高阶函数注入)。

    函数式编程以参数传递和函数的复合作为主要的表现手段,我们不需要掌握太多作为“不确定因素”存在的其他语言构造之间的交互规则,这一点对于我们的学习是有利的。

    相关文章

      网友评论

        本文标题:Java8学习必备知识——函数式编程思维三种基本构造单元和各类函

        本文链接:https://www.haomeiwen.com/subject/ohvvextx.html