4.高阶函数

扩展函数

let

  • 在函数块内可以通过 it 指代该对象
  • 返回值为函数块的最后一行或指定return表达式
object?.let{//当object不为null的条件下,才会去执行let函数体
    it.todo()
...
}

also

  • also函数let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身,一般可用于多个扩展函数链式调用
object?.also{//当object不为null的条件下,才会去执行let函数体
    it.todo()
...
}

with

  • 不像前面的函数必须使用it参数替代对象,with在函数块内可以通过 this 或省略指代该对象。返回值为函数块的最后一行或指定return表达式
inline fun <T, R> with(receiver: T, block: T.() -> R): R

可以看出with函数是接收了两个参数,分别为T类型的对象receiver和一个lambda函数块,在lambda函数块中可以对 对象receiver 进行任意引用

val result = with(caixukun, {
    println("我是篮球大使$name, 我擅长$sing $dance $rap")
    "follow me"
})
  • 在函数块中可以直接使用第一个参数对象

run

run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理.run函数接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式

inline fun <T, R> T.run(block: T.() -> R): R
list?.run{ //不需判空,内部也不需再用it代替list
   forEach { 
        println(it)
    }
}

apply

apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,apply函数的返回的是传入对象的本身,run函数是以闭包形式返回最后一行代码的值

inline fun <T> T.apply(block: T.() -> Unit): T

示例一:给控件设置属性

context.obtainStyledAttributes(attribute, R.styleable.ShapeView).apply {
    mFillColor = getColor(R.styleable.ShapeView_sv_fillColor, 0xFFFFFFFF.toInt())
    mStrokeColor = getColor(R.styleable.ShapeView_sv_strokeColor, 0)
    recycle()
}

示例二:给View绑定数据也会用到

一般写法:

val view = layoutInflater.inflate(R.layout.xx, null)
view.findViewById<TextView>(R.id.xx).text = title

apply 写法:

val view = layoutInflater.inflate(R.layout.xx, null).apply {
    findViewById<TextView>(R.id.xx).text = title
}

use

  • 该函数只能被实现了Closeable的对象使用,程序结束的时候会自动调用close方法,适合文件对象
fun main(args: Array<String>) {
    //with方式一
    val br = BufferedReader(FileReader("hello.txt")) //打开文件读取
    with(br) { //对br中的属性和方法直接进行操作
        var line: String?

        while (true) {
            line = readLine() ?: break //读取一行数据,若为空则退出循环
            println(line) //打印读取的数据
        }
        close() //关闭文件读取
    }

    //use方式二
    BufferedReader(FileReader("hello.txt")).use {
        var line: String?

        while (true) {
            line = it.readLine() ?: break //读取一行数据,若为空则退出循环
            println(line) //打印读取的数据
        }
    }

    //读取文件方式三
    println(BufferedReader(FileReader("hello.txt")).readText()) //最简单的读取文件的方法
}

map

  • 遍历每一个元素,并生成一个新的集合
val list = listOf(1,2,3,4,5,6)
//map把元素换为它的平方集合
println(list.map { it*it }) //[1, 4, 9, 16, 25, 36]
//使用map将元素为Person类型转换为String
println(people.map { it.name }) //[jack, nick, jone]
//可以使用成员引用简写
println(people.map(Person::name))   //[jack, nick, jone]

flatMap

  • 遍历每一个元素,铺平元素到一个集合中,并生成一个新的集合
   val list= listOf(1,2,3,4,5)
    val list2=list.map { listOf(it+1) }//会将list中的每一个元素都再放到一个集合中
    list2.map { println("my value is ${it}") }
    println(list2)
    println("----------------------------")
    val list3 = list.flatMap { listOf(it+1) }
    list3.map { println("my value is ${it}") }
    println(list3)

//输出:
my value is [2]
my value is [3]
my value is [4]
my value is [5]
my value is [6]
[[2], [3], [4], [5], [6]]
----------------------------
my value is 2
my value is 3
my value is 4
my value is 5
my value is 6
[2, 3, 4, 5, 6]

fold

  • fold:逐一遍历集合中的每一个元素,然后进行所需要的逻辑操作,最终得到一个结果
  • 第一个参数是遍历过的参数逻辑处理后的结构,第二个参数是现在遍历到的元素
/**
 * 第一次执行时,由初始值10作为参数a,由集合中第0个元素作为参数b
 * 第二次执行时,第一次执行的返回值作为参数a,由集合中第1个元素作为参数b
 * 依次类推...
 * 最终将结果返回
 */
val foldResult1 = arrayOf(1, 2, 3).fold(10, { a, b -> a + b })//计算过程为10+1+2+3,等于16
val foldResult2 = arrayOf(1, 2, 3).fold(10, { a, b -> a * b })//计算过程为10*1*2*3,等于60
println(foldResult1)
println(foldResult2)
/**
 * 打印输出
 * 16
 * 60
 */

reduce

  • reduce:与fold类似,区别是reduce没有初始值
  • 第一个参数是遍历过的参数逻辑处理后的结构,第二个参数是现在遍历到的元素
val reduceResult1 = arrayOf(1, 2, 3, 4).reduce { acc, i -> acc + i }//计算过程为1+2+3+4,等于10
val reduceResult2 = arrayOf(1, 2, 3, 4).reduce { acc, i -> acc * i }//计算过程为1*2*3*4,等于24
val reduceResult3 = arrayOf("a", "b", "c", "d").reduce { acc, i -> "$acc,$i" }
println(reduceResult1)
println(reduceResult2)
println(reduceResult3)
/**
 * 打印输出
 * 10
 * 24
 * a,b,c,d
 */

joinToString

  • 字符串的合并操作

为集合元素添加分隔符,组成一个新的字符串并返回:

val joinToStringResult1 = arrayOf("a", "b", "c", "d").joinToString { i -> i }
val joinToStringResult2 = arrayOf("a", "b", "c", "d").joinToString(separator = "#", prefix = "[前缀]", postfix = "[后缀]", limit = 3, truncated = "[省略号]") { i -> i }
println(joinToStringResult1)
println(joinToStringResult2)
/**
 * 打印输出
 * a, b, c, d
 * [前缀]a#b#c#[省略号][后缀]
 */

filter

遍历集合中的每一个元素,把符合要求的元素添加到新的list中,并将新list返回

//根据元素值来过滤
arrayOf(1, 2, 3, 0, 4).filter { i -> i >= 2 }.forEach(::println)
println("----------")
//根据索引来过滤
arrayOf(1, 2, 3, 0, 4).filterIndexed { index, i -> index >= 2 }.forEach(::println)
/**
 * 打印输出
 * 2
 * 3
 * 4
 * ----------
 * 3
 * 0
 * 4
 */

takeWhile

  • 遍历集合中的每一个元素,把符合要求的元素添加到新的list中,一旦遇到不符合要求的,直接终止
//注意:返回的集合中只有4和3,没有5,因为遇到2时,不符合要求,程序直接终止
arrayOf(4, 3, 2, 5).takeWhile { i -> i > 2 }.forEach(::println)
/**
 * 打印输出
 * 4
 * 3
 */

all,any,count,find,maxBy :对集合的判断应用

//数据源
val people = listOf(Person("jack", 29),
            Person("nick", 23),
            Person("jone", 26))
//年龄是否满足23
val isAge23={p:Person->p.age<=23}
//检查集合看是否所有元素满足(all)
println(people.all(isAge23)) //false
//检查集合中是否至少存在一个匹配的元素(any)
println(people.any(isAge23))//true
//检查有多少个元素满足判断式(count)
println(people.count(isAge23))//1
//找到第一个满足判断式的元素(find)
//如有多个匹配返回其中第一个元素,没有返回null。同义函数firstOrNull。
println(people.find(isAge23)) //Person(name=nick, age=23)


val people = listOf(PersonB("AAA",23),PersonB("BBB",18),PersonB("CCC",26),PersonB("DDD",5))

//找到年纪最大的元素
val maxAge = people.maxBy {  println("maxBy::${it.name},");it.age }!!.age
println("${maxAge}")



groupBy

  • 依据条件,把不同的元素划分到不同的分组

示例一:

//数据源
    val people = listOf(Person("jack", 29),
            Person("nick", 23),
            Person("jone", 23))

    //使用group按年龄分组,返回结果是map
    println(people.groupBy{it.age})
   // {29=[Person(name=jack, age=29)], 23=[Person(name=nick, age=23), Person(name=jone, age=23)]}


示例二:

val list = listOf("a", "ab", "abc", "b")
    //值得注意的是,这里的first不是String的成员,而是一个扩展(可成员引用)
    println(list.groupBy(String::first))//{a=[a, ab, abc], b=[b]}

闭包

  • 函数的运行环境,持有函数的运行状态,函数内部可以定义函数、类
  • 是支持函数式编程的基础
fun main(args: Array<String>) {
    val x = makeFun()
    x()
    x()
    x()
    x()
    x.invoke()
    //打印结果会不断++
}

//函数的状态、本地类、本地变量都可以得到保存
fun makeFun(): () -> Unit {
    var count = 0
    return fun() {
        println(++count)
    }
}

科理化

  • 作用类似可以设置默认值
  • 局限性就是在于只能设置最后一个或者几个形参,不可以是中间或者第一个形参

普通实现,手动科理化:

fun log(tag: String, target: OutputStream, msg: Any) {
    target.write("[$tag]:$msg\n".toByteArray())
}

//通过科理化将多元函数变成一元函数调用链
fun log(tag: String)
        = fun(target: OutputStream)
        = fun(msg: Any)
        = target.write("[$tag]:$msg\n".toByteArray())

fun main(args: Array<String>) {
    log("huannan", System.out, "我爱你")
    log("huannan")(System.out)("哈哈哈")
}

扩展方法实现科理化:

fun log(tag: String, target: OutputStream, msg: Any) {
    target.write("[$tag]:$msg\n".toByteArray())
}

fun <P1, P2, P3, R> Function3<P1, P2, P3, R>.curried()
        = fun(p1: P1)
        = fun(p2: P2)
        = fun(p3: P3)
        = this(p1, p2, p3)

fun main(args: Array<String>) {
    log("huannan", System.out, "我爱你")
    val logWithTag = ::log.curried()("wuhuannan")(System.out)
}

偏函数

  • 作用类似可以设置默认值
  • 和科理化相比优势:不限定为函数的哪一个形参设置默认值
fun makeString(byteArray: ByteArray, charset: Charset): String {
    return String(byteArray, charset)
}

//第一个参数固定的偏函数
fun <P1, P2, R> Function2<P1, P2, R>.partial1(p1: P1) = fun(p2: P2) = this(p1, p2)
//第二个参数固定的偏函数
fun <P1, P2, R> Function2<P1, P2, R>.partial2(p2: P2) = fun(p1: P1) = this(p1, p2)
//所有参数固定的偏函数
fun <P1, P2, R> Function2<P1, P2, R>.partialAll(p1: P1,p2: P2) = this(p1, p2)

fun main(args: Array<String>) {

    //通过偏函数固定第二个参数为charset("GBK"),其中charset是一个包级函数
    val makeStringFromGBK = ::makeString.partial2(charset("GBK"))
    makeStringFromGBK("我爱你".toByteArray())

}

综合小例子

fun main(args: Array<String>) {

    File("test.txt")
            .readText()
            //过滤空白字符
            .filterNot(Char::isWhitespace)
            //以每个字符作为key,对应出现的字作为value,进行分组,返回的是map
            .groupBy { it }
            //映射,以分组出来的map的key作为key,以map的value的数量作为值
            //返回的是一个新的map
            .map {
                it.key to it.value.size
            }
            .forEach(::println)
}

参考资料:

[常见高阶函数] https://www.jianshu.com/p/933977603e4c

[kotlin 集合的操作] https://www.jianshu.com/p/36adc63f26cf

[高阶函数] https://www.jianshu.com/p/aead59a2dbe0

[Kotlin常用的高阶函数] https://blog.csdn.net/wd2014610/article/details/79792262?utm_source=blogxgwz0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值