kotlin基础学习-7(扩展,函数式编程,序列相关)

  • 定义扩展函数
    扩展可以在不直接修改类定义的情况下增加类功能,扩展可以用于自定义类,也可以用于比如List,String,以及Kotlin标准库里的其他类。
    和继承相似,扩展也能共享类行为,在无法接触某个类定义或者某个类没有使用open修饰符,导致无法继承的时候,扩展类就是增加类功能的最好选择。
//在addExt前加上类. ,这里就是扩展了String类的方法,这个方法是,字符串调用该函数返回该字符串+number个!
fun String.addExt(number:Int) = this + "!".repeat(number)

在main函数中调用
    //因为下面新增了addExt函数,所以字符串都可以调用
    println("abc".addExt(3))
  • 超类定义扩展函数
//超类定义扩展函数
fun Any.easyPrint() = println()

在main函数中调用
    //超类定义扩展函数
    1.easyPrint()
    "jljklljkl".easyPrint()
  • 泛型扩展函数
//泛型扩展函数,这种写法非常的常见,在标准库函数中,let,also等都是这样写,使用泛型扩展,谁调用这个函数,T就是谁,并且返回当前的T
fun <T> T.easyPrint2() : T {
    println(this)
    return this
}

在main函数中调用
    //泛型扩展函数
    "asdf".easyPrint2().addExt(2).easyPrint2()
  • 扩展属性
    除了给类添加功能扩展函数外,还可以给类定义扩展属性
//给String类添加一个扩展属性,这个属性可以统计字符串里有多少个元音字母
val String.numVowel
    get() = count{"aeiou".contains(it)}

在main函数中调用
    "abcdefghijklmn".numVowel.easyPrint2()
  • 可空类型扩展函数
//可空类型扩展函数,在扩展函数内解决可能出现的空值问题
fun String?.nullableWithDefault(default: String) = println(this?:default)

在main函数中调用
    val nullable:String?= null
    nullable.nullableWithDefault("abc")
  • infix关键字
    infix关键字适用于有单个参数的扩展类函数,可以更"简洁"的调用。
    如果一个函数定义了infix关键字,那么调用它时,调用者和函数之间的.以及参数的一对括号都可以不要。
infix fun String?.nullableWithDefault2(default: String) = println(this?:default)

在main函数中调用
    //可空类型扩展函数
    val nullable:String?= null
    nullable.nullableWithDefault("abc")
    //如果函数加了infix,那么上面这个可空类型扩展函数就可以这么写了
    //    mapOf("jack" to 18),map集合的写法点击to看源码,就是用infix关键字修饰
    nullable nullableWithDefault2 "abc"
  • 定义扩展文件
    定义扩展文件,扩展函数需要在多个文件里面使用,可以将它定义在单独的文件,然后import。
    新生成扩展文件Extention.kt,如下:
package com.ftd.extention

//定义扩展文件,扩展函数需要在多个文件里面使用,可以将它定义在单独的文件,然后import
fun <T> Iterable<T>.randomTake() = this.shuffled().first()

在别的文件中调用:

package com.example.myapplication

import com.ftd.extention.randomTake as randomTake2  //这里可以重命名

fun main() {
    //定义扩展文件,扩展函数需要在多个文件里面使用,可以将它定义在单独的文件,然后import
    val list = listOf("jack","tom","jason")
    val set = setOf("java","android","python")

//    list.shuffled().first()
    //使用扩展文件里面的扩展函数
//    list.randomTake()

    //重命名扩展
    list.randomTake2()
}
  • 函数式编程
    主要依赖于高阶函数(以函数为参数或者或返回函数)返回的数据,专用于处理各种数据集合,最经典的莫过于rxjava
    函数类别
    一个函数式应用通常由三大类函数构成:变换transfrom,过滤filter,合并combine。每类函数都针对集合数据类型设计,目标式产生一个最终结果。
    函数式编程用到的函数生来都是可以组合的,也就是说,可以组合多个简单函数来构建复杂的计算行为。
    1.变换函数
    变换是函数式编程的第一大类函数,变换函数会遍历集合内容,用一个以值参形式传入的变换器函数,变换每一个元素,然后返回包含已修改元素的集合给链上的其他函数。
    最常用的两个变换函数式map和flatMap
    //map变换函数会遍历接收者集合,让变换器函数作用于集合里的各个元素,返回结果是包含已修改元素的集合,会作为链上下一个函数的输入。
    val animals = listOf("tiger","cat","dog")
    val babies = animals.map {
        animal -> "A baby $animal"
    }

    //打印可以看到,原始集合并没有被修改,map变换函数和自己定义的变化器函数做完事情后返回的是一个新集合
    println(animals)
    println(babies)

    //map返回的集合中的元素个数和输入集合必须一样,不过,返回的新集合的里的元素可以是不同类型的
    val animalLength = animals.map { it.length }
    println(animalLength)

    //flatmap函数操作一个集合的集合,将其中多个集合的元素合并后返回一个包含所有元素的单一集合
    val list = listOf(listOf(1,2,3), listOf(4,5,6)).flatMap { it }
    println(list)//这里的打印结果是[1,2,3,4,5,6]

2.过滤函数filter
过滤是函数式编程的第二大类函数,过滤函数接收一个predicate函数,用它按给定条件检查接收者集合里的元素并给出true或者false的判定。
如果返回true,受检元素就会添加到过滤函数返回的新的集合里,如果是false,就会从新集合里移除。

    //过滤集合里含有"J"的元素
    val names = listOf("Jack","Rose","Jimmy","Tom")
    val jNames= names.filter { it.contains("J") }
    println(jNames)  // 这里打印的是[Jack, Jimmy]
    
    //Demo,组合使用map和filter找质数(只能被1和自身整除)
    val numbers = listOf(4,22,7,39,53,11,55)
    val numbersZhi = numbers.filter {
        //这里的number就是集合里的每一个值,这里的第一个it指的是(2到nunber中的每一个值),第二个it指的是所有number取模后的值
            number ->
        (2 until number).map { number % it }.none { it == 0 }
    }
    println(numbersZhi)

3.合并函数
合并是函数式编程的第三大类函数,合并函数能将不同不同的集合合并成一个新集合,但是和flatmap不一样。
例如zip和fold

    //zip合并函数来合并两个集合,返回一个包含键值对的新集合
    val employee = listOf("jack","tom","rose")
    val sizes = listOf(15,20,40)
    val toMap = employee.zip(sizes).toMap()
    println(toMap["rose"])

    //fold函数用来合并值,这个合并函数接收一个初始的值,随后会根据匿名函数的结果更新
    val fold = listOf(1, 2, 3, 4).fold(0) { total, number ->
        total + number * 3
    }
    println(fold) // 这里的值 = 0+1*3 + 2*3 + 3*3 + 4*3
  • 序列
package com.example.myapplication

/**
 *
 * list,set,map集合类型,这几个集合类型统称为及早集合,这些集合的任何一个实例在创建后,它要包含的元素都会被加入并且允许访问。
 * 对应及早集合,kotlin还有另外一类集合:惰性集合。类似于类的惰性初始化,惰性集合类型的性能表现优异,尤其是用于包含大量元素的集合时,因为集合元素是按需产生的
 *
 *
 *
 *
 * 序列
 * kotlin有个内置惰性集合类型叫序列(Sequence),序列不会索引排序它的内容,也不记录元素数目。
 * 在使用一个序列时,序列里的值可能由无限多,因为某个数据源能产生无限多个元素。
 *
 *
 */


fun Int.isPrime():Boolean{
    (2 until this).map {
        if (this % it == 0){
            return false
        }
    }
    return true
}


fun main() {

    //demo,假设想产生头1000个质数。

    //针对这个需求,我们并不知道1000个质数到哪里,所以假设0-5000里就包含1000个质数。
    val take = (2..5000).toList().filter { it.isPrime() }.take(1000)
    println(take.size) // 但是此时打印结果是669,不够1000个,所以这种写法还需要加while判断

    //使用序列,这里的链式调用执行顺序应该不是顺序执行的,因为这个generateSequence方法和take方法都是Sequence重写过的,内部肯定是有关联。
    val sequenceTake = generateSequence(2) { value ->
        value + 1
    }.filter { it.isPrime() }.take(1000)
    println(sequenceTake.toList().size)//此时执行结果就是1000
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容