深入理解函数式编程

本文涉及的产品
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
简介: 深入理解函数式编程

函数式编程是对行为进行抽象。


这句话比较难理解,换句话来说:函数式编程是给自己的对象整容,有可能整的和原来差不多,也有可能整的看起来判若两人,但是只能处理这个对象,不会对函数外的其他数据产生影响。


函数式编程又结合了lambda表达式和stream API。有些朋友反馈说:函数式编程可读性不好;还有些朋友反馈说:函数式编程比较难debug。你们说的都对,但是有解决的办法,看完这篇文章就明白了。


文章整体大纲如下:

1112728-20211107164948596-265672762.png

lambda表达式


lambda表达式的本质是匿名内部类


先来看一个例子:杜甫的《登高》写的好,被称为千古律诗之首。


1112728-20211107165024457-1317188925.png


我从十几岁的时候开始就一直在想这首诗怎么不押韵:渚清沙白鸟飞回,高中老师讲过古语里“回”念huai,这就压上韵了。但是潦倒新停浊酒杯的杯在古语或者古语方言里念bai吗?直到如今我还是没有考证到是否是这样。我就当它是念bai吧。


这首诗我最喜欢的四句,渲染磅礴的气势都含了数字:万里、百年、无边、不尽。我突发奇想:让电脑来给这四句排排序吧。于是我写了下面的程序:


public void sortDengGao() {
    List<String> list = Lists.newArrayList("无边落木萧萧下","不尽长江滚滚来","万里悲秋常作客","百年多病独登台");
    list.sort(new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.substring(0, 2).compareTo(o2.substring(0, 2));
        }
    });
    System.out.println(list);
}


我鼠标点在new Comparator上,提示我匿名内部类可以用lambda代替:


1112728-20211107165059713-452043698.png


使用Alt+Enter快捷键,我回车一下,结果变成了这样:


public void sortDengGao() {
    List<String> list = Lists.newArrayList("无边落木萧萧下","不尽长江滚滚来","万里悲秋常作客","百年多病独登台");
    list.sort((o1, o2) -> o1.substring(0, 2).compareTo(o2.substring(0, 2)));
    System.out.println(list);
}


当然还可以再执行一次Alt+Enter还原回来。


匿名内部类和lambda表达式既然可以使用快捷键相互转换,那就说明他们本质上是一个东西。这样就好理解了:在jdk1.8之前,通过匿名内部类访问局部变量必须要加final关键字,jdk1.8之后不需要显示的加final关键字但实际上还是需要被访问的变量不可变。这就对应了函数式编程不会对函数外的其他数据产生影响。


lambda表达式的省略规则


lambda表达式核心是(对于匿名内部类)采用可推导可省略的原则。所以有些朋友反馈说:函数式编程可读性不好。因为它做了省略,如果对原本被省略的匿名内部类不熟悉,阅读就会麻烦些。还有一点哈,编写代码注意换行哈:


list.stream().sorted((o1, o2) -> o1.substring(0, 2).compareTo(o2.substring(0, 2))).close();


这段代码一个方法用一行是不是好看一些:


list.stream()
        .sorted((o1, o2) -> o1.substring(0, 2).compareTo(o2.substring(0, 2)))
        .close();


lambda表达式是对匿名内部类的下面三点做了省略:


  • 参数类型可省略


  • 方法只有一句代码时:大括号、return和分号可省略


  • 方法只有一个参数时小括号可以省略

 

stream API


stream API就是运用fluent风格的一个特例


对fluent风格不熟悉的强烈建议看看我之前的这篇《代码荣辱观-以运用风格为荣,以随意编码为耻》。这篇文章逻辑清晰,语言诙谐,比喻恰当,专治不明白。


这里只举个简单的例子:StringBuilder一般是这样使用的:


new StringBuilder().append(1).append(2).toString();


这是典型的fluent风格。咱们来看它包含几部分:


第一部分:new StringBuffer()构造一个特定对象


第二部分:append()对这个对象本身做处理,可以多次调用,每次都返回它本身


第三部分:toString()结束处理


再来看这个stream API的例子:


list.stream()
        .sorted((o1, o2) -> o1.substring(0, 2).compareTo(o2.substring(0, 2)))
        .close();


第一部分:.stream()构造一个特定对象


第二部分:sorted()对这个对象本身做处理,可以多次调用,每次都返回它本身


第三部分:close()结束处理


是不是一毛一样!stream API就是运用fluent风格的一个特例,如此而已。所以我们要关注的点只是.stream()构造的特定对象Stream给我提供了怎么的功能,达到了号称比sql还简单、还强大的功能。


一分钟理解MapReduce


MapReduce现在流行于大数据中的概念,本质上是为了解决对于数据的并行计算方法明明本质上都是采用分治法,但是缺少高层并行编程模型,程序员需要自行指定存储、计算、分发等任务的问题。MapReduce借鉴了Lisp函数式语言中的思想,用map和reduce两个函数提供了高层的并发编程模型的抽象。


直白点说就是提供了一个计算手脚架,照着这个架子做开发就可以了。


1112728-20211107165130098-615660692.png


上面图中可以看到map的主要功能是把数据分成小块进行计算,reduce是将小块计算结果进行合并。在stream API中map和reduce功能也是一样的。举个例子:


public void mapReduceDengGao() {
    List<String> list = Lists.newArrayList("无边落木萧萧下","不尽长江滚滚来","万里悲秋常作客","百年多病独登台");
    String result = list.stream()
            .map(word->word+"\n")
            .reduce((a,b)->a+""+b)
            .get();
    System.out.println(result);
}


上面函数先用map方法把list每个元素都进行了处理:后面加换行符。然后用reduce方法对数据合并计算:合并为一个字符串。大数据的MapReduce也就是干了这!


用Intellij对stream API做debug


有些朋友反馈说:函数式编程比较难debug。stream trace了解一下。


1112728-20211107165202546-951619435.png


首先在steam API的地方打上断点。当运行到断点处,点击上面红色框框里那个图标,之后会弹出一个框,但是可能一开始没有数据,提示正在计算,稍等一会之后stream的每一步调用结果都可以看到啦:


1112728-20211107165220990-2094181432.png


总结


函数式编程的优势:


  • 代码可读性高


  • 大数据量下处理集合效率高


  • 消灭嵌套地狱


代码可读性高,上面已经讲过了,因为简洁明了。


大数据量下处理集合效率高,这个主要是指因为函数式编程功能内聚,JVM优化时去掉了多余的锁。像上面MapReduce那段讲的,采用分治法,使用并行流的话内部做了很好的多线程处理。


消灭嵌套地狱嘛,看看下面箭头形代码:


1112728-20211107165246016-1540061755.png


用函数式编程效果是这样的,好看不好看不好说,起码sonar静态检查能过:


1112728-20211107165308243-220542380.png


相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
相关文章
|
程序员 Swift 开发者
26 函数式编程
函数式编程
63 0
|
30天前
|
机器学习/深度学习 数据采集 人工智能
函数式编程的实际应用
【10月更文挑战第12天】 函数式编程作为一种编程范式,在数据处理、金融、科学计算、Web 开发、游戏开发、物联网、人工智能等多个领域有着广泛应用。本文通过具体案例,详细介绍了函数式编程在这些领域的实际应用,展示了其在提高效率、确保准确性、增强可维护性等方面的显著优势。
108 60
|
7天前
|
数据采集 并行计算 算法
函数式编程
函数式编程是一种编程范式,它将计算视为数学函数的求值,并避免改变状态和可变数据。其核心思想是使用纯函数,减少副作用,提高代码的可读性和并行处理能力。
|
17天前
|
SQL 前端开发 测试技术
对函数式编程的深入理解
【10月更文挑战第25天】函数式编程提供了一种不同的编程思维方式,具有诸多优点,如提高代码质量、便于并发和并行编程、易于测试等。然而,它也存在一些局限性,需要根据具体的项目需求和场景来选择是否采用。随着对函数式编程的理解和应用的深入,它在现代软件开发中扮演着越来越重要的角色,为开发者提供了更多的编程选择和可能性。
8 1
|
30天前
|
并行计算 安全 数据处理
函数式编程和面向对象编程有什么区别?
【10月更文挑战第12天】 函数式编程与面向对象编程是两种不同的编程范式。前者强调数学函数的求值、不可变数据和纯函数,后者则以对象为核心,封装数据和方法。函数式编程更关注数据转换和计算过程,而面向对象编程关注对象的定义和交互。两者在数据处理、函数角色、代码结构、并发处理、灵活性和适用场景等方面存在显著差异。在实际开发中,可以根据需求选择合适的编程范式或结合使用。
36 4
|
3月前
|
自然语言处理 并行计算 大数据
什么是函数式编程
【8月更文挑战第2天】什么是函数式编程
111 13
|
并行计算 JavaScript 数据可视化
快速了解函数式编程
快速了解函数式编程
131 0
快速了解函数式编程
|
安全 Java 数据库
Lambda表达式和函数式编程
Lambda表达式和函数式编程
188 4
Lambda表达式和函数式编程
|
Scala 索引 Python
第5章 函数式编程
第5章 函数式编程
512 0
第5章 函数式编程
|
存储 Java 编译器
面向对象编程 V.S 函数式编程
面向对象编程 V.S 函数式编程
155 0
面向对象编程 V.S 函数式编程