Kotlin学习(5)函数与函数式编程

函数式编程与命令式编程最大的不同是,函数式编程的焦点在于数据的映射,命令式编程的焦点是解决问题的步骤
函数式编程是一种思维,其本质是函数的组合,例如,想要过滤出一个List中的基数,用Kotlin可以这么写:

fun main(args: Array<String>){
   val list = listOf(1, 2 ,3 ,4 ,5, 6, 7)
   println(list.filter{it % 2 == 1 })   //过滤函数,参数是一个lambda表达式
}

1. 函数式编程简介

函数式编程有如下特性:

  • 一等函数支持:函数也是一种数据类型,可以作为参数传入到另一个函数中。
  • 纯函数和不变性:纯函数是指不去函数不去改变外部的数据状态。
  • 函数的组合:在面向对象编程中是通过对象之间发送消息来构建程序逻辑的;而在函数式编程中是通过不同函数的组合来构建程序逻辑的。

2. 函数声明

就是用fun来声明,下面的例子可以更直观的表达出,函数也能做为一种变量来使用:

val sum = fun(x: Int, y: Int): Int{ return x + y}

>>>sum
(kotlin.Int, kotin.Int) -> kotlin.Int         //sun是输入是两个Int,输出类型是Int的函数
>>>sum(1, 2)
3

3. Lambda表达式

开头讲了个下面的代码:

val list = listOf(1, 2, 3, 4, 5, 6, 7)
list.filter{ it % 2 == 1}

这里的filter()函数的入参{it%2==1}就是一段 lambda表达式。
实际上,因为filter()函数只有一个参数,所以括号被省略了。所以,filter()函数调用的完整写法是:

list.filter({ it % 2 == 1})

//filter()函数声明如下:
public inline fun<T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>

其入参是一个函数 predicate:(T) -> Boolean ,实际上 {it % 2 == 1}是一种简写的语法,完整的Lambda表达式是这样写的:

{ it -> it % 2 == 1}

如果拆开来写,就更加容易理解:

val isOdd = { it: Int -> it % 2 == 1}  //直接使用Lambda表达式声明一个函数,这个函数判断输入的Int是不是奇数
val list = listOf({1, 2, 3, 4, 5, 6, 7})
>>> list.filter(isOdd)

4. 高阶函数

在λ表达式中,我们已经看到了高阶函数。现在再添加一层映射逻辑。
我们有个字符串列表:

val strList = listOf("a", "ab", "abc" ,"abcdef", "abcdefg")

然后我们想要过滤出字符串元素的长度是奇数的列表。我们把这个问题的解决逻辑拆成两个函数来组合实现:

val f = fun (x: Int) = x % 2 == 1           //判断输入的Int是否奇数
val g = fun (s: String) = s.lenth           //返回输入的字符串长度

然后我们再使用函数h 来封装"字符串元素的长度是奇数"这个逻辑,代码如下:

val h = fun(g: (String) -> Int, f: (Int) -> Boolean): (String) -> Boolean {
    return { f(g(it)) }
}

但是这个h函数声明太长了,不过Kotlin中有简单好用的Kotlin类型别名,我们使用 G、F、H来声明3个函数类型:

typealias G = (String) -> Int
typealias F = (Int) -> Boolean
typealias H = (String) -> Boolean

那么我们就可以这么写了:

val h = fun(g: G, f: F): H{
   return { f(g(it)) }    //需要注意的是 {}是不能省略的
}

在函数体的代码 { f(g(it)) }中,{}代表这就是一个Lambda表达式,返回的是一个 (String)->Boolean函数类型。如果没有 {} 那么函数返回的就是一个 Boolean值了。
那么使用就是:

println(strList.filter(h(g,f)))

5. Kotlin中的特殊函数

接下来介绍五个特殊函数, run() apply() let() also() with()

5.1 run()
run()函数返回一个 block(),其实就是调用传入的block参数,一般情况下是一个Lambda代码块。

fun testRunFun(){
   myfun()              //直接在代码行调用函数
   run ({ myfun() })    //使用run()函数调用myfun函数
   run { myfun() }      //run()函数的括号 可以省略
   run { println("Rikka") }  //等价于println("Rikka")   
}

5.2 apply()
源码显示调用 block(),在返回当前的调用者对象this。
意思是执行完block()代码块逻辑后,再次返回当前的调用者对象。测试代码示例如下:

fun testApply(){
   //普通写法
   val list = mutableListOf<String>()
   list.add("A")
   list.add("B")
   println(list)
 
   //apply()写法
   val a = ArrayList<String>().apply {
      add("A")
      add("B")
      println("$this")
   }
   println(a)
   //等价于
   a.let( println(it) )
}

5.3 let()
let函数返回了 block(this) ,意思是把当前调用者对象作为参数传入 block()中,测试代码如下:

fun testLetFun() {
    1.let{ println(it) }       //输出1
    "ABC".let{ println(it) }   //输出ABC,其中it就是调用者ABC
    myfun().let{ println(it) }  //执行fun函数
}

5.4 also()
先调用 block(this),再返回this

 val a = "ABC".also {
     println(it)    //输出ABC
 }
 println(a)

5.4 with()
with()传入了一个接受者对象 receiver,然后使用该对象receiver去调用传入的 代码块block()

//常规写法
val list = mutableListOf<String>()
list.add("A")
list.add("B")
println("$list")

//使用with()函数写法
with(ArrayList<String>()){
   add("A")
   add("B)
   println("$this")
}.let{
   println(it)  //kotlin.Unit
}
发布了248 篇原创文章 · 获赞 99 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/rikkatheworld/article/details/102910281