Android之纯函数-高阶函数简单介绍

说明:本片文章在上篇的基础上,代码在Android的环境中运行,当然可以单独写。 后面的文章就不跟Android代码联系,最后再回到Android, 写一个柯里化和函数式编程的例子。

纯函数

上篇文章提到一下纯函数, 类似两个数的加法这样:

fun add1(a: Int, b: Int): Int {
    return a + b
}
复制代码

纯函数可以理解为一种 相同的输入必定有相同的输出的函数,没有任何可以观察到副作用 纯函数的输出只取决于输入,输入定责输出定。在笔者写Android的过程中,尽量使用纯函数,较少副作用。

关于上面的函数,可以写成如下的形式: fun add2(a: Int, b: Int) = a + b

高阶函数

在函数式编程中,首先要介绍一下 高阶函数

高阶函数是将函数用作参数或者返回值的函数。Kotlin的Collection类型中有大量的这种高阶函数,例如:Iterable的filter, foreach函数。本篇文章后面也可写相应的高阶函数。

将函数当做参数

现在开始,定义一个函数,将上面的add1当做参数执行计算。 代码如下:

fun calculate(a: Int, b: Int, f: (Int, Int) -> Int) : Int {
    return f(a, b)
}
复制代码

如上, 定义了一个calculate函数, 该函数接收三个参数, 整形的a,b和作为函数的f。这比较特别,看参数定义可以知道,函数f将两个Int型值作为输入,输出一个Int值。可以进行如下调用:

codeBtn.setOnClickListener {
                when (position) {
                    0 -> {
                        codeBtn.countDown()
                    }
                    1 -> {
                        val result = calculate(2, 3, ::add1)
                        Timber.d("计算结果为: $result")
                    }
                }
            }
复制代码

上述是RecyclerView的部分代码,这里只关注1的情况,将(2,3,::add1)传入,add1前有双冒号表示当做函数参数传入。输入结果如下:

 D/CodeExampleActivity$ItemViewHolder$bindData: 计算结果为: 5
复制代码

当然也可以去除双冒号:既然函数可以当做参数或者返回值,那么相应的可以将函数理解为对象, 定义一个函数对象,执行如上相同的操作:

val add3 = {a: Int, b: Int -> a + b}
Timber.d(add3(3, 5).toString())
val result = calculate(2, 3, add3)
Timber.d("计算结果为: $result")
复制代码

log打印如下:

 D/CodeExampleActivity$ItemViewHolder$bindData: 函数对象计算结果:8
 D/CodeExampleActivity$ItemViewHolder$bindData: 计算结果为: 5
复制代码

将函数当做返回值

相比起来,我觉得当做返回值要难一些,慢慢来:

fun add4(): (Int, Int) -> Int {
    return ::add2
}

fun add5() = {
    add3
}

val add6 = add3
复制代码

首先做如上函数声明,函数add2 和函数对象 add3 用作返回值。 因为add2是函数,所以有双冒号;add3是对象,特殊在其是一个函数。

那么怎么去调用呢:

                        val addF = add4()

                        val addFF = add5()
                        Timber.d("函数add4(): ${add4()}")
                        Timber.d("函数对象addF: $addF")

                        Timber.d("函数add5(): ${add5()}")

                        Timber.d("函数对象addF计算结果:${addF(1, 2)}")
                        Timber.d("函数add4()计算结果: ${add4()(3, 4)}")


                        Timber.d("函数addFF计算结果: ${addFF()(4, 5)}")
                        Timber.d("函数add5()计算结果: ${add5()()(6, 7)}")
复制代码

log日志打印如下:

 D/CodeExampleActivity$ItemViewHolder$bindData: 函数add4(): fun add2(kotlin.Int, kotlin.Int): kotlin.Int
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数对象addF: fun add2(kotlin.Int, kotlin.Int): kotlin.Int
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数add5(): () -> (kotlin.Int, kotlin.Int) -> kotlin.Int
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数对象addF计算结果:3
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数add4()计算结果: 7
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数addFF计算结果: 9
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数add5()计算结果: 13
复制代码

在以上kotlin代码中有一个小插曲:
log打印中出现提示: Kotlin reflection is not available
解决方案:kotlin-reflection-is-not-available
build.gradle 中添加 implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

对log输出稍做解释,不过我相信没有介绍也能看懂。
函数add4() 对象addF都是 函数add2(), log中有参数和返回值信息。
而add5()是对象add3, 是一个对象,接收0个参数返回一个函数,这个函数接收两个参数返回一个值。
根据log可以简单的了解其用法,后面会介绍。

进一步改写:

fun add7(): (Int, Int) -> Int {
    return fun(a: Int, b: Int): Int {
        return a + b
    }
}

fun add8(): (Int, Int) -> Int {
    return {a: Int, b: Int -> a + b}
}

val add9 = {a: Int, b: Int -> a + b}
复制代码

这里我们直接定义匿名函数当做函数值。同样看一下log打印输出:

                        val addF = add7()

                        val addFF = add8()
                        Timber.d("函数add7(): ${add7()}")
                        Timber.d("函数对象addF: $addF")

                        Timber.d("函数add8(): ${add8()}")
                        Timber.d("函数addFF: $addFF")

                        Timber.d("函数add6(): $add9")

                        Timber.d("函数对象addF计算结果:${addF(1, 2)}")
                        Timber.d("函数add7()计算结果: ${add7()(3, 4)}")


                        Timber.d("函数addFF计算结果: ${addFF(4, 5)}")
                        Timber.d("函数add8()计算结果: ${add8()(6, 7)}")
复制代码

log输出:

 D/CodeExampleActivity$ItemViewHolder$bindData: 函数add7(): (kotlin.Int, kotlin.Int) -> kotlin.Int
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数对象addF: (kotlin.Int, kotlin.Int) -> kotlin.Int
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数add8(): (kotlin.Int, kotlin.Int) -> kotlin.Int
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数addFF: (kotlin.Int, kotlin.Int) -> kotlin.Int
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数add9: (kotlin.Int, kotlin.Int) -> kotlin.Int
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数对象addF计算结果:3
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数add7()计算结果: 7
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数addFF计算结果: 9
 D/CodeExampleActivity$ItemViewHolder$bindData: 函数add8()计算结果: 13
复制代码

这里不做解释,相信你也能看懂了。

其中有个地方需要注意,即add5()函数的定义为什么是: 函数add5(): () -> (kotlin.Int, kotlin.Int) -> kotlin.Int:

这里我再写两个函数说明一下:

val add5_1 = { 10 }

val add5_2 = 10

fun add5_1() = {
    10
}

fun add5_2(): Int {
    return 10
}
复制代码

这里要搞清楚对象和函数的区别就简单。 当返回或者等于{}时,它就是一个函数, 调用后返回最后一个对象。

                        val addF = add5_1
                        val addFF = add5_2
                        val addFFF = add5_1()
                        val addFFFF = add5_2()
                        Timber.d("$addF")
                        Timber.d("${addF()}")
                        Timber.d("$addFF")
                        Timber.d("$addFFF")
                        Timber.d("$addFFFF")
复制代码

输出如下:

 D/CodeExampleActivity$ItemViewHolder$bindData: () -> kotlin.Int
 D/CodeExampleActivity$ItemViewHolder$bindData: 17
 D/CodeExampleActivity$ItemViewHolder$bindData: 10 D/CodeExampleActivity$ItemViewHolder$bindData: () -> kotlin.Int D/CodeExampleActivity$ItemViewHolder$bindData: 10
复制代码

那么假如返回的最后一个不是数怎么办,当然,它可以是一个函数。 如下演示:

val add5_3_1 = {
    val temp = 10
    {a: Int -> a + temp}
}


fun add5_3_2() = {
    val temp = 10
    {a: Int -> a + temp}
}
复制代码

首先是add5_3_2()是一个函数,其返回值也是一个函数,该函数完成+10的功能。 如下调用:

                        Timber.d("${add5_3_1()(15)}")
                        Timber.d("${add5_3_2()()(15)}")
复制代码

现在应该能简单理解并知道结果为 25 了吧。

以上就是介绍将函数作为输入和输出的简单介绍。当然,我上面的例子由于过于浅显和简单,容易混淆,实际使用过程中也不会如上定义函数, 不过慢慢会理解的。
接下来就可以继续上述的一个事情,针对collection写自己的高阶函数。

下篇文章见

完整代码查看:
github: 纯函数 高阶函数 函数当做输入输出

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值