函数式编程与命令式编程最大的不同是 : 函数式编程的焦点在于数据的映射,命令式编程(imperative programming)的焦点是解决问题的步骤。 更重要的是一种编程思维,解决问题的思考方式,也称面向函数编程 。
函数式编程的本质是函数的组合。
函数式编程特点
函数式编程是简单、自然、直观易懂且美丽、 “优雅”的编程风格。函数式编程语言中通常都会提供常用的 map、 reduce、 filter等基本函数,这些函数是对 List、 Map 集合等基本数据结构的常用操作的高层次封装,就像一个更加智能、好用的工具箱。
函数式编程是关于不变性和函数组合的编程范式。函数式编程有如下特征:
Kotlin 中使用 fun 关键字来声明函数,其语法实例如图
声明一个函数类型的变量 sum如下:
val sum = fun(x: Int, y: Int): Int { return x + y }
先看下面这段代码 :
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>
其实, 自 filter() 函数的入参是一个函数 predicate: (T) -> Boolean(),实际上 {it % 2 ==1 } 是一种简写的语法,完整的 Lambda 表达式是这样写的 :{ it -> it % 2 == 1 }
我们有一个字符串列表:
val strList = listOf("a", "ab", "abc", "abcd", "abcde", "abcdef", "abcdefg")
过滤出 字符串元素的长度是奇数的列表。 我们把这个问题的解决逻辑拆成两个函数来组合实现 :
val f = fun(x: Int) = x % 2 == 1 //判断输入的 Int是否奇数
val g = fun (s: String) = s.length //返回输入的字符串参数的长度
再使用函数 h 来封装“字符串元素的长度是奇数”这个逻辑,实现代码如下:
val h = fun (g: (String) -> Int, f : (Int) -> Boolean) : (String) -> Boolean{
return {f(g(it))}
}
换种方式,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)) } //注意,这里的{}是不能省略的
}
这个 h 函数的映射关系可用下图来说明:
在函数体的代码 return {f(g(it))}中,{}代表这是一个 Lambda 表达式,返回的是一个 (String)->Boolean 函数类型。如果没有{},那么返回值就是一个布尔类型 Boolean 了。
在 main()函数中运行测试一下效果。
val filter = strList.filter(h(g, f))
println("filter==$filter")
//输出 filter==[a, abc, abcde, abcdefg]
run()函数的定义如下 :
public inline fun <R> run(block: () -> R): R {
contract {
callsinPlace(block, InvocationKind . EXACTLY_ONCE)
}
return block ()
}
重点看最后一行代码 block(), 其实就是调用传入的 block参数, 一般情况下是一个 Lambda 代码块。测试代码示例如下:
fun testRunFun() {
myfun () //直接在代码行调用函数
run({ myfun() })
run { myfun() }
run { println("A")}
}
fun main(args : Array<String>) {
testRunFun ()
}
运行上面的代码,输出如下 :
执行了 myfun 函数
执行了 myfun 函数
执行了 myfun 函数
A
apply()函数 的定义如下 :
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("A")
add("B")
add("C")
println("使用 apply 函数写法 this = $this")
}
println(a)
//等价于
a.let { println(it) )
}
fun main(args: Array<String>) {
testApply ()
}
运行上面 的代码,输出如下:
普通写法 list =[A, B, C]
[A, B, C]
使用 apply 函数写法 this =[A, B, C]
[A, B, C]
[A, B, C]
let()函数 的定义如下 :
public inline fun <T> T.let (block: (T) -> Unit) : T {
contract {
callsinPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
看最后一行代码 block(this),意思是把当前调用对象作为参数传 入 block()代码块中。 测试代码示例如下:
fun testLetFun() {
1.let{println(it) } //输出1, 其中it就是调用者1
"ABC".let{println(it)) } //输出ABC,其中it就是调用者ABC
//执行完函数 myfun (),返回值传给 let ()函数
}
also()函数的定义如下 :
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 testAlsoFun() {
val a ="ABC".also{
println (it) //输出: ABC
}
println(a) //输出: ABC
a.let{
println (it) //输出: ABC
}
with()函数的定义如下:
public inline fun <T, R> with(receiver: T, block: T. () -> R) : R {
contract {
callsinPlace(block, InvocationKind . EXACTLY ONCE)
}
return receiver .block ()
with()函数传入了 一个接收者对象 receiver,然后使用该对象 receiver 去调用传入的Lambda代码块 receiver.block()。测试代码如下:
fun testWithFun () {
//普通写法
val list= mutableListOf<String>()
list.add("A")
list.add("B")
list.add("C")
//常规写法 list =[A ,B, C]
println ("常规写法 list=$list")
//使用 with ()函数写法
with(ArrayList<String>()) {
add("A")
add("B")
add("C")
//使用with()函数的写法this = [A, B, C]
println ("使用 with 函数写法this = $this")
}.let {
println(it)
}
}
总结图来自下面推荐文章
关于标准函数推荐一篇文章
Kotlin:巧用内置函数let、also、with、run、apply大大提高开发效率!