首页 > 编程语言 >Kotlin中函数式编程的详解

Kotlin中函数式编程的详解

时间:2023-04-04 21:32:20浏览次数:30  
标签:函数 val Kotlin 编程 println 详解 集合 listOf

 

一、函数式编程理解
我们一直在学习面向对象编程范式,另个一个较知名的编程范式是诞生于20世纪50年代,基于抽象数学的λ(lambda)演算发展而来的函数式编程,尽管函数式编程更常用在学术而非软件领域,但它的一些原则适用于任何编程语言。函数式编程范式主要依赖于高阶函数(以函数为参数或返回函数)返回的数据,这些高阶函数专用于处理各种集合,可方便的联合多个同类函数构建链式操作以创建复杂的计算行为。Kotlin支持多种编程范式,所以你可以用混用面向对象编程和函数式编程范式来解决手头问题。

二、函数式编程类别
一个函数式应用通常由三大函数构成:变换transform,过滤filter,合并combine。每类函数都针对数据集合类型设计,目标是产生一个最终结果。函数式编程用到的函数生来就是可组合的,也就是说,你可以组合多个简单函数来构建复杂的计算行为。

1.变换函数map

fun main() {
    val animals = listOf("zebra", "giraffe", "elephant", "rat")

    /**
     * map变换函数会遍历接收者集合,让变换器函数作用于集合里的各个元素,返回结果是包含已经修改的元素的集合
     * 会作为链上下一个函数的输入
     *
     * 可以看到,原始集合animals没有被修改,map变换函数和你定义的变换函数做完事情后,返回的是一个新集合
     * 这样变量就不用变来变去了。
     * 事实上,函数式编程范式支持的设计理念就是不可变数据的副本在链上的函数间传递。
     */
    val babies = animals
        .map { animals -> "A baby $animals" }
        .map { baby -> "$baby,with the cutest little tail ever!" }
    println(animals)
    println(babies)

    /**
     * map函数返回的集合中的元素个数和输入集合必须一样,不过,返回的新集合里的元素可以是不同类型的。
     * map函数的定义源码:public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R>
     */
    val animalsLength = animals.map {
        it.length
    }
    println(animalsLength)


}

输出结果如下

[zebra, giraffe, elephant, rat]
[A baby zebra,with the cutest little tail ever!, A baby giraffe,with the cutest little tail ever!, A baby elephant,with the cutest little tail ever!, A baby rat,with the cutest little tail ever!]
[5, 7, 8, 3]

2、变换函数flatMap

fun main() {
    /**
     * flatMap函数操作一个集合中的集合,将其中多个集合中的元素合并后返回一个包含所有元素的单一集合。
     */
    val result = listOf(listOf(1, 2, 3), listOf(4, 5, 6)).flatMap {
        it
    }
    println(result)

    /**
     * 变换
     * 变换是函数式编程的第一大类函数,变换函数会遍历集合内容,用一个以值参形式传入的变换器函数
     * 然后返回已经包含已修改元素的集合给链上的其他函数。
     * 最常用的两个变换函数时map和flatMap
     */
}

输出结果如下

[1, 2, 3, 4, 5, 6]

3、过滤函数filter

fun main() {
    /**
     * 过滤是函数式编程的第二大函数,过滤函数接受一个predicate函数,用它按给定条件
     * 检查接收者集合里的元素并给出true或false的判定。如果predicate函数返回true
     * 受检元素就会添加到过滤函数返回的新集合里。如果predicate函数返回false,那么
     * 受检元素就被移出新集合。
     */
    val result = listOf("Jack", "Jimmy", "Rose", "Tom")
        .filter { it.contains("J") }
    println(result)

    val items = listOf(
        listOf("red apple", "green apple", "blue apple"),
        listOf("red fish", "green fish"),
        listOf("yellow banana", "teal banana")
    )

    /**
     * filter过滤函数接受一个predicate函数,在flatMap遍历它的输入集合中的所有元素时,
     * filter函数会让predicate函数按过滤条件,将符合条件的元素都放入它返回的新集合里。
     * 最后,flatMap会把变换函数返回的子集合合并在一个新集合里。
     */
    val readItems = items.flatMap {
        it.filter {
            it.contains("red")
        }
    }
    println(readItems)
}

输出结果如下

[Jack, Jimmy]
[red apple, red fish]

4、组合使用filter和map函数找素数

fun main() {
    /**
     * 找素数,除了1和它本身,不能被任何数整除的数。仅使用了几个简单的函数,
     * 我们就解决了找素数这个比较复杂的问题,这就是函数式编程的独特魅力:每个函数
     * 做一点,组合起来就能干大事。
     *
     * 除了1和它本身,不能被任何数整除的数
     * 取模等于0,说明能够整除,如果没有一个是等于0的,说明是素数
     */
    val numbers = listOf(7, 4, 8, 4, 3, 33, 18, 11)
    val primes = numbers.filter { number ->
        (2 until number).map {
            number % it
        }.none { it == 0 }
    }
    println(primes)
}

输出结果如下

[7, 3, 11]

5、合并函数zip

/**
 * @Author: ly
 * @Date: 2023/2/6
 * @Description: 合并是函数式编程的第三大类函数,合并函数能将不同的集合合并成一个新集合,
 *                这和接收者是包含集合的集合的flatMap函数不同
 */
fun main() {
    val employees = listOf("Jack", "Jason", "Tommy")
    val shirtSize = listOf("large", "x-large", "medium")
    val ages = listOf(20, 30, 19)

    /**
     * zip合并函数来合并两个集合,返回一个包含键值对的新集合
     * 让后可以通过toMap()函数,转换为map集合
     */
    val newList = employees.zip(shirtSize).toMap()
    val employeesAges = employees.zip(ages).toMap()
    println(newList["Jack"])
    println(employeesAges["Jason"])
}

输出结果如下

large
30

6、合并函数fold

fun main() {
    /**
     * fold函数
     * 这个合并函数接受一个初始累加器值,随后会根据匿名函数的结果更新。
     * 将每个元素值乘以3累加起来
     */
    val foldedValue = listOf(1, 2, 3, 4).fold(0) { accumulator, number ->
        println("Accumulator value:$accumulator")
        accumulator + (number * 3)
    }
    println("Final value:$foldedValue")
}

输出结果如下

Accumulator value:0
Accumulator value:3
Accumulator value:9
Accumulator value:18
Final value:30

三、为什么要使用函数式编程?
为什么要使用函数式编程
乍看之下,实现同样的任务,Java版本和函数式版本的代码量差不多,但仔细分析一下
就能看出函数式版本的诸多优势。
1.累加变量都是隐式定义的。
2.函数运算结果会自动赋值给累加变量,降低了代码出错的机会。
3.执行新任务的函数很容易添加到函数调用链上,因为他们都兼容Iterable类型。

1、相同的一个需求使用java实现如下

public class FunctionTest {
    public static void main(String[] args) {
        List<String> keys = Arrays.asList("Jack", "Jason", "Tommy");
        List<String> values = Arrays.asList("large", "x-large", "medium");
        HashMap<String, String> hashMap = new HashMap<>();
        for (int i = 0; i < keys.size(); i++) {
            hashMap.put(keys.get(i), values.get(i));
        }
        System.out.println(hashMap);

        List<String> newLists = new ArrayList<>();
        for (Map.Entry<String, String> entry : hashMap.entrySet()) {
            newLists.add(String.format("%s shirt size %s", entry.getKey(), entry.getValue()));
        }
        System.out.println(newLists);
    }
}

输出结果如下

{Jason=x-large, Jack=large, Tommy=medium}
[Jason shirt size x-large, Jack shirt size large, Tommy shirt size medium]

2、使用kotlin就很简洁,代码如下

fun main() {
 val employees = listOf("Jack", "Jason", "Tommy")
    val shirtSize = listOf("large", "x-large", "medium")

    /**
     * zip合并函数来合并两个集合,返回一个包含键值对的新集合
     * 让后可以通过toMap()函数,转换为map集合
     */
    val newList = employees.zip(shirtSize).toMap()

    /**
     *为什么要使用函数式编程
     * 乍看之下,实现同样的任务,Java版本和函数式版本的代码量差不多,但仔细分析一下
     * 就能看出函数式版本的诸多优势。
     * 1.累加变量都是隐式定义的。
     * 2.函数运算结果会自动赋值给累加变量,降低了代码出错的机会。
     * 3.执行新任务的函数很容易添加到函数调用链上,因为他们都兼容Iterable类型。
     */
    val list = newList.map {
        "${it.key},shirt size:${it.value}"
    }
    println(list)
}

输出结果如下

[Jack,shirt size:large, Jason,shirt size:x-large, Tommy,shirt size:medium]


标签:函数,val,Kotlin,编程,println,详解,集合,listOf
From: https://blog.51cto.com/u_15880918/6169680

相关文章

  • 如何编写高质量的 JS 函数(3) --函数式编程[理论篇]
    作者:杨昆 【编写高质量函数系列】中,《如何编写高质量的JS函数(1)--敲山震虎篇》介绍了函数的执行机制,此篇将会从函数的命名、注释和鲁棒性方面,阐述如何通过JavaScript编写高质量的函数。 《如何编写高质量的JS函数(2)--命名/注释/鲁棒篇》从函数的命名、注释和鲁棒性方面,阐......
  • OpenTSDB 数据存储详解
    作者:DuZhimin随着互联网、尤其是物联网的发展,我们需要把各种类型的终端实时监测、检查与分析设备所采集、产生的数据记录下来,在有时间的坐标中将这些数据连点成线,往过去看可以做成多纬度报表,揭示其趋势性、规律性、异常性;往未来看可以做大数据分析,机器学习,实现预测和预警。这些数......
  • docker run 参数详解
    命令格式:dockerrun[OPTIONS]IMAGE[COMMAND][ARG...]Usage:Runacommandinanewcontainer中文意思为:通过run命令创建一个新的容器(container)常用选项说明-d,--detach=false,指定容器运行于前台还是后台,默认为false-i,--interactive=false,打开STDIN,用于控制台交互-t,-......
  • 异步编程之事件循环机制
    JavaScript是一门单线程语言,我们可以通过异步编程的方式来实现实现类似于多线程语言的并发操作。本文着重讲解通过事件循环机制来实现多个异步操作的有序执行、并发执行;通过事件队列实现同级多个并发操作的先后执行顺序,通过微任务和宏任务的概念来讲解不同阶段任务执行的先后顺序,......
  • MYSQL-Explain详解
    --实际SQL,查找用户名为Jefabc的员工select*fromempwherename='Jefabc';--查看SQL是否使用索引,前面加上explain即可explainselect*fromempwherename='Jefabc';expain出来的信息有10列,分别是id、select_type、table、type、possible_keys、key、key_len、ref......
  • 「刷起来」Go必看的进阶面试题详解
    勤学如春起之苗,不见其增日有所长;辍学如磨刀之石,不见其损日有所亏。本文的重点:逃逸分析、延迟语句、散列表、通道、接口。1.逃逸分析逃逸分析是Go语言中的一项重要优化技术,可以帮助程序减少内存分配和垃圾回收的开销,从而提高程序的性能。下面是一道涉及逃逸分析的面试题及其详......
  • Python——异步编程案例
    摘要主要是讲解Python中的异步编程的下的实际的案例案例:异步操作redis案例:异步操作MySQL案例:FastAPl框架异步案例:异步爬虫课程总结......
  • python——异步编程代码实战
    摘要主要介绍python中相关的异步编程的原理和是代码的实战协程实现协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行。协程不是计算机提供,程序员人为创造。协程的优点:在一个线程中如果遇到IO等待时间,线程不......
  • 并发编程——JUC并发大厂面试问题
    摘要现如今,不管是应届毕业生还是工作了三五年之内的工程师,在面试招聘的时候JUC并发编程的是必须掌握的一个技能,否者你将会被面试官玩弄。本博文将整理有关于的JUC的大厂面试问题和答案。帮助大家在面试过程中能够回答面试官问题的一二。同时本人也总结相关的面试问题的在相关文档中......
  • SQL Server 数据库T-SQL编程
    1、T-SQL编程通过SQL语句来完成业务的处理,执行编写好的sql语句,就可以完成业务处理。2、局部变量SQLserver中变量分为:局部变量和全局变量。全局变量,在全局可用,系统自定义,用户不可以定义全局变量,用不不可以修改全局变量,全局变量以“@@”开头局部变量就是一个能够拥有......