高阶函数的基本概念
应用方式(3种)
- 直接双冒号::printlin
// 这种引用适用于lambda表达式只有一个函数调用并且
// 这个函数的参数也是这个lambda表达式的参数
args.forEach (::println)
- 类名双冒号函数名的方法引用的方法通常要包含自己的实例作为第一个参数,比如扩展方法函数;
1、
fun String?.isEmpty(): Boolean = this == null || this == ""
val isEmpty: (String) -> Boolean = String::isEmpty
2、
class Hello{
fun world(){
println("Hello World.")
}
}
//引用
val helloWorld = Hello::world
- 实例双冒号和方法名,这时候第一个实例是调用实例对象(Kotlinv1.1开始支持,v1.1以前不支持)带有Receiver的引用pdfPrinter::println
1、
class PdfPrinter{
fun println(any: Any){
kotlin.io.println(any)
}
}
//引用
val pdfPrinter = PdfPrinter()
args.forEach(pdfPrinter::println)
常见高阶函数
//forEach函数使用的一般结构
object.forEach{
//todo
}
例:
val list = listOf(1,3,5,10,18,36,2,9)
list.forEach(::println)
- map:接受一个lambda表达式,并且有返回值,形成新的集合
//map函数使用的一般结构
object.map{
//todo
}
例:
val newList = list.map{
it * 2 + 3//相当于返回值
}
val newList2 = list.map(Int::toDouble)
//flatMap函数使用的一般结构
object.flatMap {
it
//todo
}
或
object.flatMap {
it.map {
//todo
}
}
例:
val list = listOf(
1..20,
2..15,
100..166)
val flatList = list.flatMap{
it
}
val flatList = list.flatMap{
it.map{
"No.$it"
}
}
- reduce:使用reduce对集合进行计算操作(无初始值)
//reduce函数使用的一般结构
object.reduce{acc,i ->
//acc 为累计的结果
//acc todo
}
例:
list.reduce{acc,i -> acc + i}
fun factorial(n: Int):Int{
if(n==0) return 1
return (1..n)reduce{acc,i -> acc * i}
}
//对0-6的数分别求阶乘,并且打印出来
(0..6).map(::factorial).forEach(::println)
- fold:使用fold对集合进行自定义计算(有初始值)
//fold函数使用的一般结构
object.fold([初始值]){acc,i ->
//todo
}
例:
// 对0-6 的阶乘进行求和并加上初始值 5
println((0..6).map(::factorial).fold(5){acc,i ->
acc + i
})
//字符串拼接
println((0..6).map(::factorial).fold(StringBuilder()){acc,i ->
acc.append(i).append(",")
})
// 字符串连接
println((0..6).joinToString(","))
//foldRight函数使用的一般结构
object.foldRight([初始值]){i,acc ->
//todo
}
例:
//字符串拼接
println((0..6).map(::factorial).foldRight(StringBuilder()){i,acc ->
acc.append(i).append(",")
})
- filter:传入Lambda表达式为true时,保留该元素;使用filter对集合进行按条件过滤
//filter函数使用的一般结构
object.filter{
//todo
}
例:
//给一个长度为 n 的数组,每个元素都在 [1,n] 之间,要求找出 [1,n] 中没有在数组里出现的元素。
fun findDisappearNumbers(nums: IntArray): List {
val ifAppear = BooleanArray(nums.size + 1)
nums.forEach { ifAppear[it] = true }
return ifAppear.mapIndexed {
index, boolean ->
if (boolean || index == 0) -1 else index
}.filter { it != -1 }
}
- takeWhile:循环遍历集合,直到第一个不满足条件的数据时,停止循环
//takeWhile函数使用的一般结构
object.takeWhile{
//todo
}
- let:let扩展杉树实际上是一个作用域函数,当需要定义一个变量在一个特定的作用域范围内,可以使用let函数;let函数另一个作用就是可以避免写一些判断null的操作。
//let函数使用的一般结构
object.let{
it.todo()//在函数体内使用it替代object对象去访问其公有的属性和方法
...
}
//另一种用途 判断object为null的操作
object?.let{//表示object不为null的条件下,才会去执行let函数体
it.todo()
}
例:
data class Person(val name: String,val age:Int){
fun work(){
println("$name is working!!!")
}
}
fun findPerson():Person?{
return null
}
fun main(args: Array){
findPerson()?.let{person ->
person.work()
println(person.age)
}
}
- apply:apply一般用于对一个对象实例初始化的时候需要对对象中的属性进行赋值;一般可用于多个扩展函数链式调用;数据bean多层级包裹判空处理的问题
//apply函数使用的一般结构
object.apply{
//todo
}
//例:
//1、
data class Person(val name: String,val age:Int){
fun work(){
println("$name is working!!!")
}
}
fun findPerson():Person?{
return null
}
fun main(args: Array){
findPerson()?.apply{
work()
println(age)
}
}
//2、
mSectionMetaData?.apply{
//mSectionMetaData不为空的时候操作mSectionMetaData
}?.questionnaire?.apply{
//questionnaire不为空的时候操作questionnaire
}?.section?.apply{
//section不为空的时候操作section
}?.sectionArticle?.apply{
//sectionArticle不为空的时候操作sectionArticle
}
- with:适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可
//with函数使用的一般结构
with(object){
//todo
}
例:
class MyLogger {
var tag: String = "TAG"
fun e(msg: String) {
println("$tag $i")
}
fun tag(tagStr: String) {
tag = tagStr
}
}
fun main(args: Array) {
val logger = MyLogger()
with(logger) {
tag("Kotlin")
e("It is a good language")
}
}
- run :run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理
//run函数使用的一般结构
object.run{
//todo
}
1、
val str = "kotlin"
str.run {
println( "length = ${this.length}" )//6
println( "first = ${first()}")//k
println( "last = ${last()}" )//n
}
2、 为TextView设置属性。
val mTvBtn = findViewById(R.id.text)
mTvBtn.run{
text = "kotlin"
textSize = 13f
...
}
- use :use函数作用于现实了Closeable接口的类,比如文件io操作
//use函数使用的一般结构
object.use{
//todo
}
例:
var l = BufferedReader(FileReader("123.txt")).use {
var line: String = ""
while (true){
line += it.readLine()?: break
}
line
}
println(l)
尾递归优化
- 递归的一种特殊形式——调用了自身后无其他操作
- tailrec关键字提示编译器尾递归优化(编译器转换为迭代进行编译)
- 尾递归和迭代的关系:递归表达的清晰,迭代执行效率高,递归有些内存开销
闭包
- 函数运行的环境
- 持有函数运行的状态
- 函数内部可以定义函数,也可以定义类
函数复合(f(g(x)))
Currying
fun log(tag:String,target:OutputStream,message:Any?){
target.write("[$tag]: $message\n".toByteArray())
}
fun curriedLog(tag:String):(target:OutputStream) -> (message:Any?){
//todo
}
//fun log(tag:String)
// =fun (target:OutputStream)
// =(message:Any?)
// =target.write("[$tag]: $message\n".toByteArray())
fun Function3.curried()
=fun(p1:P1)=fun(p2:P2)=fun(p3:P3)=this(p1,p2,p3)
fun main(args: Array){
log("Test",System.out,"Hello World!")
// log("Test")(System.out)("Hello World Currying")
::log.curried()("Test")(System.out)("Hello World Currying")
}
偏函数
fun log(tag:String,target:OutputStream,message:Any?){
target.write("[$tag]: $message\n".toByteArray())
}
fun Function3.curried()
=fun(p1:P1)=fun(p2:P2)=fun(p3:P3)=this(p1,p2,p3)
fun main(args: Array){
val consoleLog = (::log.cueried())("Test out")(System.out)
consoleLog("Hello 偏函数!")
}