kotlin从入门到看开 ₆


layout: post
title: "kotlin-高阶函数"
subtitle: "这个世界属于有天赋的人 也属于认真的人 更属于那些在有天赋的领域认真钻研的人"


高阶函数

高阶函数:传入或者返回函数的函数

函数引用的三种方式

  1. 包级函数作为高阶函数的参数的时候,直接按照参数返回值来判断是否合适.
  2. 类的成员函数(带有Receiver的函数)作为高阶函数的参数的时候,需要使用实例来进行引用.
  3. 扩展方法作为高阶函数的参数的时候,需要注意的是扩展方法有一个默认的参数就是实例本身
fun test() {
        val line = "-------"
        val arrayInt = listOf(11, 22, 33, 44, 55)
        var arrayStr = listOf("杏本诗歌", "守凪了子", "西宫结弦", "花畑佳子", "冈部伦太郎", "")
        /**
         * 包级函数
         */
        arrayInt.forEach(::println)
        println(line)
        /**
         * 类的成员函数
         */
        val pm = MyPrintln()
        arrayStr.filter { it.contains("子") }.forEach(pm::println)
        arrayStr.filter { it.length > 4 }.forEach(pm::println)
        println(line)
         /**
         * 扩展方法
         */
        arrayStr.filter(String::isNotBlank).forEach(::println)
        println(line)
    }

<font size="2" color="#006666">包级函数</font>

map

  1. map:集合的映射
  2. map:集合的转换
        val arrayInt1 = arrayInt.map { it * 4 }
        val arrayDouble = arrayInt.map(Int::toDouble)
        println(arrayInt1)
        println(arrayDouble)

flatMap

  1. flatMap:扁平化集合
  2. ... RangeTo
  3. 1..5:返回一个intRange(1,2,3,4,5)
        val list = arrayListOf(1..9, 233..255, 79..88)
        val flatMap = list.flatMap { it }
        flatMap.forEach(::println)

reduce

  1. reduce通常用于求和
        println(flatMap.reduce(Int::plus))
        println(flatMap.reduce { acc, i -> acc + i })
        //阶乘示例
        println(factorial(8))

阶乘

    /**
     * 阶乘
     */
    private fun factorial(n: Int): Int {
        if (n == 0) return 1
        return (1..n).reduce { acc, i -> acc * i }
    }

flod

  1. fold:对集合进行自定义计算
        println((0..1).fold(8) { acc, i -> acc + i })

        /**
         * joinToString:对集合进行转化和拼接
         */
        println((0..10).joinToString(","))
        val fold = (0..5).fold(StringBuilder()) { acc, i -> acc.append(i).append(",") }
        println(fold)

字符串拼接

        println((0..10).joinToString(","))

filter

  1. filter用于过滤,传入表达式的值为true时保留:
        val pm = MyPrintln()
        arrayStr.filter { it.contains("子") }.forEach(pm::println)
        arrayStr.filter { it.length > 4 }.forEach(pm::println)

takeWhile

  1. takeWhile通常用于带有条件的循环遍历,直到第一个不满足条件元素出现循环结束.
         //直到第一个不小于44的元素出现,循环结束.
         //最后输出元素应为(11,22,33)
        val arrayInt = listOf(11, 22, 33, 44, 55)
        arrayInt.takeWhile { it<44 }.forEach(::println)

补充说明

<p id = "package"></p>

  • 包级函数:在包内直接声明函数的方式叫作包级函数
package com.litton.ishir

inline fun print(message: Int) {
    System.out.println(message)
}

data class

    data class singer(var name: String, var song: String) {
        fun sing() {
            println("$name,演唱新单曲$song")
        }
    }

let

  • let: it表示引用对象,可调用其成员(属性以及方法),it不可省略.
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

    
KaryNg.let {}   //在函数体内使用it替代引用对象去访问其公有的属性和方法
KaryNg?.let {}  //另一种用途:?判断对象是否为空,不为空才会执行let函数体

例子:

    fun main(args: Array<String>) {
        val KaryNg = singer("吴雨霏", "我本人")
        val kary: Any = KaryNg?.let {
            it.song
            it.name.length
        }
        println(kary)
    }

输出:

singer(name=吴雨霏, song= <<我本人>>)

3

适用场景:

使用let函数处理需要针对一个可null的对象统一做判空处理.

官方源码:

/**
 * Calls the specified function [block] with `this` value as its argument and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

also

  • also: it表示引用对象,可调用其成员(属性以及方法),it不可省略.</br>
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

also函数结构实际上和let很像唯一的区别就是返回值不一样,let以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值.而also函数则返回传入对象本身.

        KaryNg.run {  }

例子:

    fun main(args: Array<String>) {
        val KaryNg = singer("吴雨霏", "我本人")
        val kary: Any = KaryNg?. also {
            it.song
            it.name.length
        }
        println(kary)
    }

输出:

吴雨霏,演唱新单曲 <<我本人>></br>
singer(name=吴雨霏, song= <<我本人>>)

适用场景:

一般可用于多个扩展函数链式调用

官方源码:

/**
 * Calls the specified function [block] with `this` value as its argument and returns `this` value.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

with

  • with: 在函数块内通过 this 指代对象,调用成员(属性以及方法),this可省略.</br>
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

with函数和前面的函数使用方式略有不同,它不是以扩展函数.它将对象作为函数的参数

        with(KaryNg){ sing() }

例子:

    fun main(args: Array<String>) {
        val KaryNg = singer("吴雨霏", "我本人")
        val sing=with(KaryNg){
            println(this)
            sing()
            name
        }
            println(sing)
    }

输出:

singer(name=吴雨霏, song= <<我本人>>)</br>
吴雨霏,演唱新单曲 <<我本人>> </br>
吴雨霏

适用场景:

适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上

官方源码:

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

run

  • run: 在函数块内通过 this 指代对象,调用成员(属性以及方法),this可省略.</br>
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

        KaryNg.run {  }

例子:

    fun main(args: Array<String>) {
        val KaryNg = singer("吴雨霏", "我本人")
        val kary: Any = KaryNg.run {
                println(this)
                sing()
                name.length
            }
            println(kary) 
     }

输出:

singer(name=吴雨霏, song= <<我本人>>)</br>
吴雨霏,演唱新单曲 <<我本人>> </br>
3

适用场景:

适用于let,with函数任何场景.run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理

官方源码:

/**
 * Calls the specified function [block] with `this` value as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

apply

  • with: 在函数块内通过 this 指代对象,调用成员(属性以及方法),this可省略.</br>
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

从结构上来看apply函数和run函数很像,唯一不同就是它们返回值不一样,run函数以闭包形式返回最后一行代码的值,而apply函数的返回传入对象本身

        KaryNg.apply {  }

例子

    fun main(args: Array<String>) {
        val KaryNg = singer("吴雨霏", "我本人")
        val kary: Any = KaryNg.apply {
            song
            sing()
        }
        println(kary)
    }

输出

吴雨霏,演唱新单曲我本人</br>
singer(name=吴雨霏, song=我本人)

适用场景:

apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值.或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见.特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到.

官方源码

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

use

未完结

  • use: 在函数块内通过 it 指代对象,调用成员(属性以及方法),it不可省略.</br>
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

例子

 val read = BufferedReader(FileReader("build.gradle")).use {
            val readText = it.readLine()
            readText
        }
        println(read)

输出

apply plugin: 'com.android.application'

适用场景:

use自己关流.

官方源码

/**
 * Executes the given [block] function on this resource and then closes it down correctly whether an exception
 * is thrown or not.
 *
 * @param block a function to process this [Closeable] resource.
 * @return the result of [block] function invoked on this resource.
 */
@InlineOnly
@RequireKotlin("1.2", versionKind = RequireKotlinVersionKind.COMPILER_VERSION, message = "Requires newer compiler version to be inlined correctly.")
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    var exception: Throwable? = null
    try {
        return block(this)
    } catch (e: Throwable) {
        exception = e
        throw e
    } finally {
        when {
            apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
            this == null -> {}
            exception == null -> close()
            else ->
                try {
                    close()
                } catch (closeException: Throwable) {
                    // cause.addSuppressed(closeException) // ignored here
                }
        }
    }
}

闭包

  1. 函数运行的环境
  2. 持有函数运行状态
  3. 函数内部可以定义函数
  4. 函数内部也可以定义类

复合函数

m(x) = f(g(x))

  • g = g(x) P1:即g函数的参数{x} P2:即g函数的返回值{g}== P2
  • f = f(g) P2:即f函数的参数{g}== P2 R:即f函数的返回值{f}== R
  • m = m(x) P1:即g函数的参数{x}
    fun onTest() {
        //m(x) = f(g(x))
        val g = { i: Int -> i + 7 } //g(x) = x+7
        val f = { i: Int -> i * 2 } //f(g) = g*2
        val m = g composite f //m(x) = (x+7)*2
        val m1 = f composite g //m(x) = (x*2)+7
        val value = m(2)
        val value1 = m1(2)
        println(value)
        println(value1)
    }

/**
 * @param P1
 * @param P2
 * @param R
 */
infix fun <P1, P2, R> Function1<P1, P2>.composite(function: Function1<P2, R>): Function1<P1, R> {
    return fun(p1: P1): R {
        return function.invoke(this.invoke(p1))
    }
}

柯里化

多元函数变换成一元函数调用链

fun log(tag: String) = fun(target: OutputStream) = fun(message: Any) = target.write("$tag-$message\n".toByteArray())
 
fun log1(tag:String,target:OutputStream,message: Any){
    target.write("$tag-$message \n".toByteArray())
}
 
fun main(args: Array<String>) {
    log("日志")(System.out)("记录日志1")
    ::log1.curried()("日志")(System.out)("记录日志2")
 
}
 
fun <P1, P2, P3, R> Function3<P1, P2, P3, R>.curried() = fun(p1: P1) = fun(p2: P2) = fun(p3: P3) = this(p1, p2, p3)

偏函数

传入部分参数得到新函数

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,739评论 2 9
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,163评论 9 118
  • 前文介绍了 Java 8 新增的 Stream 及其使用方式。本文汇总一下 Stream 各个方法经常用到的函数接...
    程序之心阅读 721评论 0 2
  • 20183月份最神奇的事,某天梦里见到一个不熟到认得的人,两天后居然在楼下遇见了。现实中和此人相遇的几率很小。
    karenthefull阅读 172评论 0 0
  • 还有一个半小时,我会起身走向导师办公室,用一副已经被录取的姿态和他谈一谈如何在学术方面更近一层。再三个小时,我会走...
    InkInk阅读 201评论 0 1