Kotlin入门之高阶函数

作者: wxyass 分类: Android,Kotlin 发布时间: 2018-03-15 10:55

一 高阶函数的基本概念

传入或者返回函数的函数

函数引用 ::println

带有Receiver的引用 pdfPrinter::println

fun main(args: Array<String>) {
    args.forEach(::println)

    val helloWorld = Hello::world

    args.filter(String::isNotEmpty)

    val pdfPrinter = PdfPrinter()
    args.forEach(pdfPrinter::println)
}

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

class Hello{
    fun world(){
        println("Hello World.")
    }
}

二 常见的高阶函数1

fun main(args: Array<String>) {

    // 一个整型集合
    val list = listOf(1,3,5,10,8,2)

    // 使用forEach组建新的集合
    val newList = ArrayList<Int>()
    list.forEach {
        val newElement = it * 2 + 3
        newList.add(newElement)
    }
    newList.forEach(::println)

    // 使用map组建新的集合
    val newList2 = list.map {
        it * 2 + 3
    }
    newList2.forEach(::println)
    list.map(::println)

    // 使用map组建另一类型的集合
    val newList3 = list.map {
        it.toDouble()
    }
    newList3.forEach(::println)

    // 与newList3一样
    val  newList4= list.map (Int :: toDouble)
    newList4.forEach(::println)

    // 一个整型集合的集合
    val list2 = listOf(
            1..20,
            2..5,
            100..322
    )

    // 使用flatMap组建新的集合
    val flatList = list2.flatMap {
        it
    }
    flatList.forEach(::println)

    // 使用flatMap组建新的集合
    val flatList2 = list2.flatMap {
        it.map {
            "No.$it"
        }
    }
    flatList2.forEach(::println)

    // 同flatList2  使用flatMap组建新的集合
    val flatList3 = list2.flatMap { intRange ->
        intRange.map { intElement ->
            "No.$intElement"
        }
    }
    flatList3.forEach(::println)

    // 求和
    val sum = flatList.reduce { acc, i -> acc + i }
    println(sum) // 47277

    // 0到6的阶乘
    (0..6).map(::factorial).forEach(::println)
    // 1        0   (1)=1
    // 1        1   (1*1)=1
    // 2        2   (1*2)=2
    // 6        3   (2*3)=6
    // 24       4   (6*4)=24
    // 120      5   (24*5)=120
    // 720      6   (120*6)=720

    // 阶乘后求和
    val sum2 = (0..6).map(::factorial).reduce { acc, i -> acc + i }
    println(sum2) // 874

    // 使用fold设定一个初始值 再相加
    val sum3 = (0..6).map(::factorial).fold(5) { acc, i -> acc + i }
    println(sum3) // 879

    // 使用fold拼接字符串
    val string = (0..6).map(::factorial).fold(StringBuffer()) {
        acc, i -> acc.append(i).append(",")
    }
    println(string) // 1,1,2,6,24,120,720,

    // 拼接字符串
    val string2 = (0..6).joinToString(",")
    println(string2) // 0,1,2,3,4,5,6

    // 使用foldRight 倒序拼接字符串 注意参数调换位置
    val string3 = (0..6).map(::factorial).foldRight(StringBuffer()) {
        i,acc  -> acc.append(i).append(",")
    }
    println(string3) // 720,120,24,6,2,1,1,


    //使用filter过滤,将阶乘中的奇数保留
    println((0..6).map(::factorial).filter { it % 2== 1 })
    // [1, 1]

    // 使用filterIndexed过滤,将阶乘中的奇数位置上的数保留
    println((0..6).map(::factorial).filterIndexed { index, i ->  index % 2== 1} )
    // [1, 6, 120]

    // takeWhile 只要遇到一个不符合条件的,不在向后取了,只取前面符合条件的
    // (比如:取奇数,只要遇到偶数就停止)
    println((0..6).map(::factorial).takeWhile { it % 2 == 1} )
    // [1, 1]

}

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

三 常见的高阶函数2

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

fun main(args: Array<String>) {

    // let
    findPerson()?.let {persion->
        println(persion.name)
        println(persion.age)
    }

    // let
    findPerson()?.let {(name,age)->
        println(name)
        println(age)
    }

    // apply 直接调用方法 属性
    findPerson()?.apply {
        work()
        println(age)
    }

    // with 参数不是空
    val br = BufferedReader(FileReader("Hello.txt"))
    with(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)
        }
    }

}

fun findPerson(): Person?{
    return null
}

map/flatMap
fold/reduce
filter/takeWhile
let/apply/with/use

四 尾递归优化

递归的一种特殊形式
调用自身后无其他操作
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)
}

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)
}

// 阶乘
fun factorial(n: Long): Long{
    return n * factorial(n - 1)
}

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)
}

五 闭包(函数式编程的福音)

函数运行的环境
持有函数运行状态
函数内部可以定义函数
函数内部也可以定义类

val string = "HelloWorld"

fun makeFun(): ()->Unit{
    var count = 0
    return fun(){
        println(++count)
    }
}

fun fibonacci(): () -> Long{
    var first = 0L
    var second = 1L
    return fun():Long{
        val result = second
        second += first
        first = second - first
        return result
    }
}

fun fibonacci2(): 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>) {

    val x = makeFun()
    x()
    x()
    x()
    x()
    x()

    val y = fibonacci()
    println(y())
    println(y())
    println(y())
    println(y())
    println(y())

    for (i in fibonacci2()){
        if (i>100) break
        println(i)
    }


   val add5 = add(5)
    println(add5(2))
}

//fun add(x: Int) = fun(y: Int) = x + y

fun add(x: Int): (Int)-> Int{

    data class Person(val name: String, val age: Int)

    return fun(y: Int): Int{
        return x + y
    }
}

六 函数复合

f(g(x))

//f(g(x))   m(x) = f(g(x))
val add5 = {i: Int -> i + 5} // g(x)
val multiplyBy2 = {i : Int -> i * 2} // f(x)

fun main(args: Array<String>) {
    println(multiplyBy2(add5(8))) // (5 + 8) * 2

    val add5AndMultiplyBy2 = add5 andThen multiplyBy2
    println(add5AndMultiplyBy2(8)) // m(x) = f(g(x))

    val add5ComposeMultiplyBy2 = add5 compose  multiplyBy2
    println(add5ComposeMultiplyBy2(8)) // m(x) = g(f(x))
}

// 中缀 Function1 扩展方法  
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)-函数调用联

简单说就是多元函数变成一元函数调用链

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

// 柯里化
fun log1(tag: String)
    = fun(target: OutputStream)
    = fun(message: Any?)
    = target.write("[$tag] $message\n".toByteArray())

fun main(args: Array<String>) {
    // 常规
    log("benny", System.out, "HelloWorld")

    // 柯里化
    log1("benny")(System.out)("HelloWorld1")

    // 柯里化
    ::log.curried()("benny")(System.out)("HelloWorld2")

}

// 扩展方法  柯里化
fun <P1, P2, P3, R> Function3<P1, P2, P3, R>.curried()
    = fun(p1: P1) = fun(p2: P2) = fun(p3: P3) = this(p1, p2, p3)


[benny] HelloWorld
[benny] HelloWorld1
[benny] HelloWorld2

八 偏函数

传入部分参数得到的新函数

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

// 柯里化
fun log1(tag: String)
    = fun(target: OutputStream)
    = fun(message: Any?)
    = target.write("[$tag] $message\n".toByteArray())

fun main(args: Array<String>) {
    // 常规
    log("benny", System.out, "HelloWorld")

    // 柯里化
    log1("benny")(System.out)("HelloWorld1")

    // 柯里化
    ::log.curried()("benny")(System.out)("HelloWorld2")

    // 偏函数
    val consoleLogWithTag = (::log.curried())("benny")(System.out)
    consoleLogWithTag("HelloWorld3")
    consoleLogWithTag("HelloWorld4")
    consoleLogWithTag("HelloWorld5")
    consoleLogWithTag("HelloWorld6")

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

// 扩展方法  柯里化
fun <P1, P2, P3, R> Function3<P1, P2, P3, R>.curried()
    = fun(p1: P1) = fun(p2: P2) = fun(p3: P3) = this(p1, p2, p3)

val makeString = fun(byteArray: ByteArray, charset: Charset): String{
    return String(byteArray, charset)
}

// 偏函数
val makeStringFromGbkBytes = makeString.partial2(charset("GBK"))

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>.partial1(p1: P1) = fun(p2: P2) = this(p1, p2)


[benny] HelloWorld
[benny] HelloWorld1
[benny] HelloWorld2
[benny] HelloWorld3
[benny] HelloWorld4
[benny] HelloWorld5
[benny] HelloWorld6
我是中国人

九 小案例:统计字符串个数程序开发

fun main(args: Array<String>) {
    val map = HashMap<Char, Int>()

    File("build.gradle")
            .readText()
            .toCharArray()
            .filterNot(Char::isWhitespace)
            .forEach {
        val count = map[it]
        if(count == null)map[it]=1
        else map[it] = count + 1
    }
    map.forEach(::println)


    File("build.gradle")
            .readText()
            .toCharArray()
            .filterNot(Char::isWhitespace)
            .groupBy { it }
            .map { it.key to it.value.size}
            .forEach(::println)
}

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

邮箱地址不会被公开。 必填项已用*标注