函数式编程语言最重要的基础是lambda演算,而且lambda演算的函数可以传入函数参数,也可以返回一个函数。函数式编程(简称FP)是一种编程范式。
函数式编程与命令式编程最大的不同是:函数式编程的焦点在于数据的映射,命令式编程的焦点是解决问题的步骤。函数式编程不仅仅指的是某一种语言,更重要的是一种编程思想,解决问题的思考方式,也称面向函数编程。
1、函数式编程简介
函数式编程是关于不变性和函数组合的编程范式。函数式编程有如下特性:
(1)一等函数支持:函数也是一种数据类型,可以作为参数传入另一个函数中,同时函数也可以返回一个函数。
(2)纯函数和不变性:纯函数指的是没有副作用的函数(函数不去改变外部的数据状态)。例如,一个编译器就是一个广义上的纯函数。在函数式编程中,倾向于使用纯函数编程。正因为纯函数不会去修改数据,同时又使用不可变的数据,所以程序不会取修改一个已经存在的数据结构,而是根据一定的映射逻辑创建一份新的数据。函数式编程是转换数据而非修改原始数据。
(3)函数的组合:在面向对象编程中是通过对象之间发送消息来构建程序逻辑的;而在函数式编程中是通过不同函数组合来构建程序逻辑的。
2、声明函数
// 函数声明
fun sum(a : Int,b : Int) : Int {
return a + b
}
println("函数声明:sum = " + sum(3,7))
// 输出:I/System.out: 函数声明:sum = 10
fun:声明函数的关键字
sum:函数名
a : Int,b : Int:函数参数列表
: Int:函数返回值
return a + b:函数体
函数也可以作为变量来使用,声明一个函数类型的变量sum:
// 声明一个函数类型的变量sum
val sum = fun(a : Int, b : Int) : Int {
return a + b
}
println("声明一个函数类型的变量:sum = " + sum(30,79))
可以看出这个函数变量sum的类型是(Int,Int)-> Int。这个带箭头"->"的表达式就是一个函数类型,表示一个输入两个Int类型值,输出一个Int类型值得函数。
3、Lambda表达式
一个例子:
// Lambda表达式
val intList = listOf<Int>(1,2,3,4,5,6,7,8,9,0)
println("列表中奇数的数字有 :" + intList.filter( { it % 2 == 1}))
// 因为filter函数只有一个参数,所以也可以去掉(),既intList.filter{ it % 2 == 1}
// 输出
I/System.out: 列表中奇数的数字有 :[1, 3, 5, 7, 9]
这里的filter()函数的入参{ it % 2 == 1}就是一段Lambda表达式。Lambda表达式需要用{}符号。
以上是一种简写的Lambda表达式,完整的Lambda表达式是:
{ it -> it % 2 == 1}
拆开下如下:
// 拆开Lambda表达式
val isOdd = {it : Int -> it % 2 == 1}
println("列表中奇数的数字有 :" + intList.filter( isOdd))
4、高阶函数
高阶函数就是将 函数 作为 参数 或 返回值的函数。
lambda表达式中filter函数的参数是一个函数,类型为predicate: (T) -> Boolean,其原型为:
所以 filter为高阶函数,下面的例子,查看列表中字符串,列出其长度为偶数的字符串。
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
// 高阶函数
val f = fun(x : Int) = x % 2 == 0 // 判断输入的int是否为奇数。 函数声明:输入参数为int类型,函数体:x % 2 == 0
val g = fun(s : String) = s.length // 获取输入的字符串的长度。 函数声明: 输入参数为String类型,函数体: s.length
// 声明 函数h,两个参数,分别为函数g 和 函数f。输出为(String)-> Boolean类型。函数体:return { f(g(it)) }
// 函数类型参数:g 函数名,(String)函数g输入类型,Int 函数g的输出类型
val h = fun(g : (String) -> Int, f : (Int) -> Boolean) : (String)-> Boolean {
return { f(g(it)) }
}
// 函数调用
var list = listOf("udydgdh","hfidhd","gfhfjdhggdudud","fhfiahihfiaifagif","gfys")
// h(g,f)类似于数学公式。
println("列表中,长度为偶数的字符串有:" + list.filter (h(g,f)))
// 输出:
I/System.out: 列表中,长度为偶数的字符串有:[hfidhd, gfhfjdhggdudud, gfys]
注意:在函数体的代码return { f(g(it)) }中,{}代表这是一个Lambda表达式,返回的是一个(String)-> Boolean 函数类型。如果没有{},那么返回值就是一个布尔类型Boolean。
5、Kotlin 中的特殊函数
本节介绍kotlin中的run() apply() let() also() with() 这5个特殊的函数。使用的测试代码如下:
fun myFun() : String {
println("执行了myfun函数")
return "这是myfun函数的返回值"
}
(1)run()函数
定义如下:
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
重点看最后一行block(),其实就是调用传入的block参数,一般情况下是一个Lambda函数。测试代码如下:
fun testRunFun() {
println("ceshi run1")
println(myFun()) // 直接在代码行调用函数
println("ceshi run2")
run({myFun()}) // 使用run()函数调用myfun()函数
println("ceshi run3")
run {myFun()} // run()函数的()可以省略
println("ceshi run4")
run {println("A")} // 等价于println("A")
}
直接结果:
(2)apply()函数
函数定义如下:
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
重点看最后两行代码,显示调用了block()函数,然后返回当前的调用者对象this。意思是执行完block()代码块逻辑后,再次返回当前的调用者对象。测试代码:
fun testApply() {
// 普通写法
val list = mutableListOf<String>()
list.add("A")
list.add("B")
list.add("C")
println("普通写法 list = $list")
println(list)
// 使用apply()
val a = ArrayList<String>().apply { // 调用apply函数
add("D")
add("E")
add("F")
println("使用apply()函数写法 this = $this")
}
println(a)
// 等价于
a.let { println(it) }
}
测试结果:
(3)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(this),意思是把当前调用对象作为参数传入block()代码块中。测试代码如下:
fun testLet() {
1.let{ println(it)} // 输出1,其中it就是调用者1
"ABC".let { println(it) } // 输出“ABC”,其中it就是调用者ABC
myFun().let { println(it) } // 执行完myFun()函数后,返回值传给let()函数
}
测试结果:
(4)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)
return this
}
最后两句,首先是调用了block(this),类似let()函数的逻辑,但是最后返回值是this,也就是当前的调用者。测试代码:
fun testAlso() {
var a = "ABC".also { println(it) }
println("a = $a")
a.let { println(it) }
}
测试结果:
(5)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()
}
最后两行,首先是调用了block(this),类似let()函数的逻辑,但是最后返回的值是this,也就是当前的调用者。测试代码如下:
fun testWith() {
// 普通写法
val list = mutableListOf<String>()
list.add("A")
list.add("B")
list.add("C")
println("普通写法 list = $list")
println(list)
// 使用with()函数
with(ArrayList<String>()) {
add("D")
add("E")
add("F")
println("使用with函数写法 this = $this")
}.let { println(it) } // kotlin.Unit
}
测试结果: