Kotlin 函数与函数式编程(读书笔记)

第5章 函数与函数式编程

函数式编程与命令式编程最大的不同是 : 函数式编程的焦点在于数据的映射,命令式编程(imperative programming)的焦点是解决问题的步骤。 更重要的是一种编程思维,解决问题的思考方式,也称面向函数编程 。
函数式编程的本质是函数的组合。

函数式编程特点
函数式编程是简单、自然、直观易懂且美丽、 “优雅”的编程风格。函数式编程语言中通常都会提供常用的 map、 reduce、 filter等基本函数,这些函数是对 List、 Map 集合等基本数据结构的常用操作的高层次封装,就像一个更加智能、好用的工具箱。

5.1 函数式编程简介

函数式编程是关于不变性和函数组合的编程范式。函数式编程有如下特征:

  • 一等 函数支持 Cfirst-class function) : 函数也是一种数据类型,可以作为参数传入另一个函数中,同时函数也可以返回一个函数
  • 纯函数( pure 缸nction)和不变性( immutable) : 纯函数指的是没有副作用的函数(函数不去改变外部的数据状态〉。例如,一个编译器就是一个广义上的纯函数。在函数式编程中,倾向于使用纯函数编程。正因为纯函数不会去修改数据,同时 又使用不可变的数据,所以程序不会去修改一个已经存在的数据结构,而是根据 一定 的映射逻辑创建一份新的数据。函数式编程是转换数据而非修改原始数据
  • 函数的组合(compose function):在面向对象编程中是通过对象之间发送消息来 构建程序逻辑的;而在函数式编程中是通过不同函 数的组合来构建程序逻辑的

5.2 声明函数

Kotlin 中使用 fun 关键字来声明函数,其语法实例如图
Kotlin 函数与函数式编程(读书笔记)_第1张图片
声明一个函数类型的变量 sum如下:

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

5.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>

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

5.4 高阶函数

我们有一个字符串列表:

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 函数的映射关系可用下图来说明:
Kotlin 函数与函数式编程(读书笔记)_第2张图片
在函数体的代码 return {f(g(it))}中,{}代表这是一个 Lambda 表达式,返回的是一个 (String)->Boolean 函数类型。如果没有{},那么返回值就是一个布尔类型 Boolean 了。

在 main()函数中运行测试一下效果。

val filter = strList.filter(h(g, f))
println("filter==$filter")  
//输出 filter==[a, abc, abcde, abcdefg]

5.5 Kotlin 中特殊函数

5.5.1 run() 函数

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

5.5.2 apply() 函数

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]

5.5.3 let() 函数

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 ()函数
}

5.5.4 also() 函数

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
	}

5.5.5 with() 函数

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 函数与函数式编程(读书笔记)_第3张图片
关于标准函数推荐一篇文章
Kotlin:巧用内置函数let、also、with、run、apply大大提高开发效率!

你可能感兴趣的:(Kotlin)