Kotlin中函数式编程的详解

简介: Kotlin中函数式编程的详解

一、函数式编程理解

我们一直在学习面向对象编程范式,另个一个较知名的编程范式是诞生于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]


目录
相关文章
|
6天前
|
人工智能 Dart Java
Kotlin基础语法
Kotlin基础语法
|
9月前
|
安全 Java Android开发
kotlin学习和常用知识
@[TOC](目录) Kotlin 是一种由 JetBrains 公司开发的静态类型编程语言,旨在为 Java 开发者提供一种更加安全、简洁、易于使用的编程语言。Kotlin 可以编译成 Java 字节码,因此可以在 Java 虚拟机(JVM)上运行。下面是关于 Kotlin 的详细解释、发展历史、语法学习和代码解读。 # 一、Kotlin 详解 1. 基本概念 Kotlin 是一种静态类型语言,这意味着在编译时会对代码进行类型检查,从而减少了在运行时的错误。Kotlin 也支持面向对象编程,具有封装、继承、多态等特性。此外,Kotlin 还支持函数式编程,具有高阶函数、Lambda 表达式、
137 0
|
Java 开发者 Kotlin
Kotlin中lambda表达式详解
lambda运算时java后面版本引进的,所以实现的仅仅是从形式上简化代码,内部的优化并不是非常出色,而Kotlin一开始就支持函数式编程,使得其lambda表达式具有性能上的优势,同时Kotlin简洁的风格也给lambda表达式进一步简化提供了一个良好的实现方式,下面带大家具体看看他们之间的区别以及如何更好的使用Kotlin的极简化lambda表达式
135 0
Kotlin中lambda表达式详解
|
安全 IDE Java
又被 Kotlin 语法糖坑惨的一天
又被 Kotlin 语法糖坑惨的一天
168 0
又被 Kotlin 语法糖坑惨的一天
|
Java Kotlin
学习Kotlin一
学习Kotlin一
71 0
学习Kotlin一
|
Java Kotlin 索引
【Kotlin 初学者】函数式编程
一、函数式编程概念 1.1 面向函数编程(FOP) 在函数式编程(FP)中,一切皆是函数。FP是关于不变性和函数组合的一种编程范式。 函数式语言提倡在有限的几种关键数据结构(如list、set、map)上,运用函数的组合(高阶函数)操作,自底向上地来构建世界。Kotlin支持多种编程范式,所以你可以混用面向对象编程和函数式编程范式来解决手头的问题。
126 0
【Kotlin 初学者】函数式编程
|
存储 安全 Java
【Kotlin 初学者】泛型简单剖析与使用
一、泛型使用 泛型,即 &quot;参数化类型&quot;,将类型参数化,可以用在类,接口,函数上。 与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。
209 0
【Kotlin 初学者】泛型简单剖析与使用
|
Java 编译器 Kotlin
聊聊Kotlin中的元编程
聊聊Kotlin中的元编程
279 0
聊聊Kotlin中的元编程
|
Java 编译器 Kotlin
Kotlin之DataClass学习
Kotlin之DataClass学习
164 0
|
Java Kotlin 编译器
Kotlin 基础语法
Kotlin 文件以 .kt 为后缀。 包声明 包的声明应处于源文件顶部: package my.demo import java.util.* // …… kotlin源文件不需要相匹配的目录和包,源文件可以放在任何文件目录。
1023 0