Kotlin 进阶之路9 高阶函数

Kotlin 进阶之路 目录

高阶函数的基本概念

  • 传入或者返回函数的函数
  • 函数引用 ::println
  • 带有Receiver的引用 pdfPrinter::println
fun main(args: Array<String>) {

    val country = arrayOf("Britain", "France", "China", "Japan", "American", "Germany")
    //1.直接引用
    country.forEach(::println)
    println("--------------------------------")

    //2.类名引用    Hello类名 String::isNotEmpty  String 类名 
    val helloWorld = Hello::world
    country.filter(String::isNotEmpty).forEach(::println)

    println("--------------------------------")
    //3.调用者引用方法
    val pdfPrinter = PdfPrinter()
    country.forEach(pdfPrinter::println)
}

class PdfPrinter {
    fun println(any: Any) {
        kotlin.io.println(any)
    }
}

class Hello {
    fun world() {
        println("Hello World.")
    }
}
Britain
France
China
Japan
American
Germany
--------------------------------
Britain
France
China
Japan
American
Germany
--------------------------------
Britain
France
China
Japan
American
Germany

常见高阶函数

  • map/flatMap
  • fold/reduce
  • filter/takeWhile
  • let/apply/with/use
fun main(args: Array<String>) {
    val list = listOf(1, 3, 4, 5, 10, 8, 2)

    //常规写法 啰嗦
//    val newList = ArrayList<Int>()
//    list.forEach {
//        val newElement = it * 2 + 3
//        newList.add(newElement)
//    }

    println("----------------map----------------")
    //高阶函数
    val newList = list.map { it * 2 + 3 }
    newList.forEach(::println)

    val newList2 = list.map(Int::toDouble)
    newList2.forEach(::println)

    println("----------------flatMap----------------")
    val list3 = listOf(1..4, 2..6, 100..102)
    list3.forEach(::println)

    val flatList = list3.flatMap { it }
    flatList.forEach(::println)


    val flatList2 = list3.flatMap { intRange ->
        intRange.map { intElement -> "No.$intElement" }
    }
    flatList2.forEach(::println)

    println("----------------求和----------------")
    println(flatList.reduce { acc, i -> acc + i })

    println("----------------求阶乘----------------")
    (0..6).map(::factorial).forEach(::println)

    println("----------------fold 给一个初始值,各阶乘求和----------------")
    println((0..6).map(::factorial).fold(5) { acc, i ->
        acc + i
    })

    println("----------------fold 拼接----------------")
    println((0..6).map(::factorial).fold(StringBuilder()) { acc, i ->
        acc.append(i).append(",")
    })
    println("----------------joinToString 拼接----------------")
    println((0..6).joinToString(","))

    println("----------------foldRight倒着拼接----------------")
    println((0..6).map(::factorial).foldRight(StringBuilder()) { i, acc ->
        acc.append(i).append(",")
    })
    println("----------------filter 过滤 值符合条件的数----------------")
    println((0..6).map(::factorial).filter { it % 2 == 1 })

    println("----------------filterIndexed 过滤数所在位置符合条件的数----------------")
    println((0..6).map(::factorial).filterIndexed { index, i -> index % 2 == 1 })

    println("----------------takeWhile 取符合条件的数直到----------------")
    println((0..6).map(::factorial).takeWhile { it % 2 == 1 })

}

fun factorial(n: Int): Int {
    if (n == 0) return 1
    return (1..n).reduce { acc, i -> acc * i }
}
----------------map----------------
5
9
11
13
23
19
7
1.0
3.0
4.0
5.0
10.0
8.0
2.0
----------------flatMap----------------
1..4
2..6
100..102
1
2
3
4
2
3
4
5
6
100
101
102
No.1
No.2
No.3
No.4
No.2
No.3
No.4
No.5
No.6
No.100
No.101
No.102
----------------求和----------------
333
----------------求阶乘----------------
1
1
2
6
24
120
720
----------------fold 给一个初始值,各阶乘求和----------------
879
----------------fold 拼接----------------
1,1,2,6,24,120,720,
----------------joinToString 拼接----------------
0,1,2,3,4,5,6
----------------foldRight倒着拼接----------------
720,120,24,6,2,1,1,
----------------filter 过滤 值符合条件的数----------------
[1, 1]
----------------filterIndexed 过滤数所在位置符合条件的数----------------
[1, 6, 120]
----------------takeWhile 取符合条件的数直到----------------
[1, 1]
import java.io.BufferedReader
import java.io.FileReader

data class Person(val name: String, val age: Int) {
    fun work() {
        println("$name is working!!!")
    }
}

fun main(args: Array<String>) {
    println("----------------let----------------")
    findPerson()?.let { person ->
        println(person.name)
        println(person.age)
    }

    findPerson()?.let { (name, age) ->
        println(name)
        println(age)
    }
    findPerson()?.let { person ->
        person.work()
        println(person.age)
    }
    println("----------------apply----------------")
    findPerson()?.apply {
        work()
        println(age)
    }
    println("----------------with----------------")
    val br = BufferedReader(FileReader("E:\\hello.txt"))
    with(br) {
        var line: String?
        while (true) {
            line = readLine() ?: break
            println(line)
        }
        close()
    }


    println("----------------use----------------")
    BufferedReader(FileReader("E:\\hello.txt")).use {
        var line: String?
        while (true) {
            line = it.readLine() ?: break
            println(line)
        }
    }
    println("----------------readText----------------")
    val brNew = BufferedReader(FileReader("E:\\hello.txt")).readText()
    println(brNew)
}

fun findPerson(): Person? {
    return Person("John", 28)
}
----------------let----------------
John
28
John
28
John is working!!!
28
----------------apply----------------
John is working!!!
28
----------------with----------------
你好!
----------------use----------------
你好!
----------------readText----------------
你好!

尾递归优化

  • 递归的一种特殊形式
  • 调用自身后无其他操作
  • tailrec关键字提示编译器尾递归优化
  • 尾递归与迭代的关系
data class ListNode(val value: Int, var next: ListNode? = null)

 tailrec fun findListNode(head: ListNode?, value: Int): ListNode? {
    head ?: return null
    if (head.value == value) return head
    return findListNode(head.next, value)
}


data class TreeNode(val value: Int) {
    var left: TreeNode? = null
    var right: TreeNode? = null
}

fun findTreeNode(root: TreeNode?, value: Int): TreeNode? {
    root ?: return null
    if (root.value == value) return root
    return findTreeNode(root.left, value) ?: return findTreeNode(root.right, value)
}

fun main(args: Array<String>) {
    val MAX_NODE_COUNT = 100000
    val head = ListNode(0)
    var p = head
    for (i in 1..MAX_NODE_COUNT) {
        p.next = ListNode(i)
        p = p.next!!
    }

    println(findListNode(head, MAX_NODE_COUNT - 2)?.value)
}
99998

注:当MAX_NODE_COUNT数很大 而去掉 tailrec关键字,会报错
Exception in thread "main" java.lang.StackOverflowError
at chapter5.tailrecursive.MainKt.findListNode(Main.kt:8)
at chapter5.tailrecursive.MainKt.findListNode(Main.kt:8)
at chapter5.tailrecursive.MainKt.findListNode(Main.kt:8)
at chapter5.tailrecursive.MainKt.findListNode(Main.kt:8)
at chapter5.tailrecursive.MainKt.findListNode(Main.kt:8)
at chapter5.tailrecursive.MainKt.findListNode(Main.kt:8)
at chapter5.tailrecursive.MainKt.findListNode(Main.kt:8)
at chapter5.tailrecursive.MainKt.findListNode(Main.kt:8)

闭包

  • 函数运行的环境
  • 持有函数运行状态
  • 函数内部可以定义函数
  • 函数内部也可以定义类
fun fibonacci(): Iterable<Long> {
    var first = 0L
    var second = 1L
    return Iterable {
        object : LongIterator() {
            override fun nextLong(): Long {
                val result = second
                second += first
                first = second - first
                return result
            }

            override fun hasNext() = true
        }
    }
}

fun main(args: Array<String>) {

    for (i in fibonacci()) {
        if (i > 100) break
        println(i)
    }
    println("--------------------------------")
    val add5 = add(5)
    println(add5(2))
}

fun add(x: Int): (Int) -> Int {
    data class Person(val name: String, val age: Int)
    return fun(y: Int): Int {
        return x + y
    }
}
1
2
3
5
8
13
21
34
55
89
--------------------------------
7

函数复合

  • f(g(x))
  • 如何实现函数符合
  • 回顾:infix的使用
val add5 = { i: Int -> i + 5 } //f(x)
val multiplyBy2 = { i: Int -> i * 2 }//g(x)

fun main(args: Array<String>) {
    println(multiplyBy2(add5(8))) //(5 + 8)* 2
    val add5AndMultiplyBy2 = add5 andThen multiplyBy2
    val add5ComposeMultiplyBy2 = add5 compose multiplyBy2
    println(add5AndMultiplyBy2(8)) //m(x) =f(g(x))
    println(add5ComposeMultiplyBy2(8)) //m(x)=g(f(x))
}

/**
 * P1 是参数
 * P2 是参数
 * R  是回调参数
 * */
infix fun <P1, P2, R> Function1<P1, P2>.andThen(function: Function1<P2, R>): Function1<P1, R> {
    return fun(p1: P1): R {
        return function.invoke(this.invoke(p1))
    }
}

infix fun <P1, P2, R> Function1<P2, R>.compose(function: Function1<P1, P2>): Function1<P1, R> {
    return fun(p1: P1): R {
        return this.invoke(function.invoke(p1))
    }
}
26
26
21

Currying

  • 理解Currying的概念
- 简单说就是多元函数变换成一元函数调用链
  • 了解Currying的实现方法
import java.io.OutputStream

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

//原方案
//fun log(tag:String)
//   = fun(target:OutputStream)
//   = fun(message:Any?)
//   = target.write("[$tag] $message\n".toByteArray())

//curried方案
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("kpioneer", System.out, "HelloWorld")
    //  log("kpioneer")(System.out)("HelloWorld Again.")
    ::log.curried()("kpioneer")(System.out)("HelloWorld Again.")
}
[kpioneer] HelloWorld
[kpioneer] HelloWorld Again.

偏函数

  • 理解偏函数的概念
- 传入部分参数得到新的函数
  • 仔细体会与Currying的不同
  • 了解偏函数的实现方法
import java.io.OutputStream
import java.nio.charset.Charset

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

//原方案
//fun log(tag:String)
//   = fun(target:OutputStream)
//   = fun(message:Any?)
//   = target.write("[$tag] $message\n".toByteArray())

//curried方案
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("kpioneer", System.out, "HelloWorld")
//    //  log("kpioneer")(System.out)("HelloWorld Again.")
//    ::log.curried()("kpioneer")(System.out)("HelloWorld Again.")
//
//    val consoleLogWithTag = (::log.curried())("kpioneer")(System.out)
//    consoleLogWithTag("HelloWorld Again.")

    val bytes = "我是中国人".toByteArray(charset("GBK"))
    val stringFromGBK = makeStringFromGbkByte(bytes)
    println(stringFromGBK)
}

val makeString = fun(byteArray: ByteArray, charset: Charset): String {
    return String(byteArray, charset)
}
val makeStringFromGbkByte = makeString.partial2(charset("GBK"))

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

推荐阅读更多精彩内容