简介
- 函数式编程是一种编程范式,它与命令式编程最大的不同是,函数式编程的焦点在于数据的映射,命令式编程的焦点是解决问题的步骤,函数式编程强调的是一种编程思维,解决问题的思考方式,也叫面向函数编程
eg:
@Test
fun test() {
val list = listOf(1, 2, 3, 4, 5)
// 函数式编程打印偶数
print(list.filter { it % 2 == 0 }) // [2, 4]
// 命令式编程打印偶数
for (i in list) {
if (i % 2 == 0) {
println(i) // 2 4
}
}
}
}
- 通过比较可以看出函数式编程更直观易懂且优雅
函数式编程特征
- 一等函数支持:函数也是一种数据类型,可以作为参数传入另一个函数中,同时函数也可以返回一个函数
- 纯函数和不变性:纯函数指的是没有副作用的函数(即不改变原始的数据)例如:一个编译器就是一个广义上的纯函数。在函数式编程中,倾向于使用纯函数编程,因为纯函数不会去修改数据,同时又使用不可变的数据,所以程序不会去修改一个已经存在的数据结构,而是根据一定的映射逻辑创建一份新的数据,函数式编程是转换数据而非修改原始数据
- 函数的组合:在面向对象编程中是通过对象之间发送消息来构建程序逻辑的;而在函数式编程中是通过不同函数的组合来构建程序逻辑的
eg:
// 两者等价
fun sum(x: Int, y: Int): Int {
return x + y
}
val sum = fun(x: Int, y: Int): Int { return x + y }
- 由此可知,Kotlin是一种面向表达式的语言
Lambda表达式
list.filter { it % 2 == 0 }
- 这里面的list.filter()函数的入参{it % 2 == 0}就是Lambda表达式的简写形式,因为filter()函数只有一个参数
- 其中filter函数声明如下:
// filter函数的入参是一个函数predicate:(T)->Boolean
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
// 对入参中的元素进行判断是否符合条件,符合条件的放入目标容器中
for (element in this) if (predicate(element)) destination.add(element)
return destination
}
- 完整写法如下
list.filter({it -> it % 2 == 0})
高阶函数
eg:想要过滤出字符串列表中长度为奇数的字符串
val strList = listOf("a ", "ab ", "abc ", "abcd", "abcde", "abcdf ", "abcdefg")
val f = fun(x: Int): Boolean = x % 2 == 1
val g = fun(s: String) : Int = s.length
val h = fun(g: (String) -> Int, f: (Int) -> Boolean): (String) -> Boolean {
return { f(g(it)) } // {}代表返回的是一个lambda表达式,类型是(String)->boolean类型,如果没有{},返回的就是一个Boolean类型
}
// 使用Kotlin的别名可以简写成如下形式
typealias G = (String) -> Int
typealias F = (Int) -> Boolean
typealias H = (String) -> Boolean
val h = fun (g : G, f : F) : H {
return {f(g(it))}
}
@Test
fun testH() {
print(strList.filter(h(g, f))) // [ab , abcde, abcdefg]
}
- 复合函数h的映射关系
Kotlin中的特殊函数
run()函数
- run函数定义如下
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block() // 直接调用传入的lambda表达式
}
- run函数使用
fun myfun() :String {
println("myfun is running")
return "myfun return value"
}
@Test
fun testRunFun() {
// 直接调用myfun函数
myfun() // myfun is running
// 使用run调用myfun函数
run({myfun()}) // myfun is running
// run函数的括号传入的是一个参数可以省略
run{myfun()} //myfun is running
// 等价于pprintln("A")
run{println("A")} // A
}
apply()函数
- apply函数定义如下
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block() // 调用传入的lambda表达式
return this // 返回函数的调用者,类似于builder模式
}
- apply函数的使用
@Test
fun testApply() {
val list = ArrayList<String>().apply {
add("A")
add("B")
add("C")
}
println(list) // [A, B, C]
}
let函数
- let函数定义如下
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this) // 把当前的调用对象传入block中
}
- let函数的使用
@Test
fun testLet() {
"ABC".let{println(it)} // ABC
myfun().let { println(it) } // myfun is running myfun return value
}
also函数
- also函数定义如下
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this) // 把当前的调用对象传入block中
return this // 返回函数的调用者
}
- also函数的使用
@Test
fun testAlso() {
"ABC".also { println("1" + it.also { println("2" + it) }) }.let { println(it) } // block(this)打印2ABC返回ABC调用者,然后执行1ABC返回调用者
}
with函数
- with函数定义如下
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
- with函数的使用
@Test
fun testWith() {
with(ArrayList<String>()) {
add("A")
add("b")
add("C")
println("$this") // 返回给receiver即当前函数调用者 [A, b, c]
}
}