前篇系列文章(请同学们按照顺序来学习):
一起从零学Kotlin-20170728:
http://blog.csdn.net/clandellen/article/details/76283527
一起从零学Kotlin-20170730:
http://blog.csdn.net/clandellen/article/details/76369434
一起从零学Kotlin-20170801:
http://blog.csdn.net/clandellen/article/details/76519661
链接:http://pan.baidu.com/s/1geVxlJ1 密码:4nht
官方API才是学习的王道……
Intellij IDEA(kotlin开发学习IDEA,并且还支持Java)下载
https://www.jetbrains.com/idea/
官网下载慢的可以从我云盘里面下载:
链接:http://pan.baidu.com/s/1i4Au2nb 密码:qato
什么是函数引用呢?回顾一下前面,我们知道一个函数或者方法的调用除了给这个方法传递基本类型的引用,引用类型引用之外,还可以传递Lambda表达式,甚至也可以是函数的引用,看到这里有些摸不着头脑是吧!笔者刚学的时候也是这样摸不着头脑,懂了之后才发现原来如此简单,就是我们把函数和方法不要局限于传统之中。看看下面的代码,你就能懂了:
fun main(args: Array) {
var ints:IntArray = intArrayOf(1,2,3,4,5)
ints.myForEach(::println)
//ints.myForEach(::shuChuString)
//这段语句会报错,因为函数shuChuString()对应的Lambda表达式的类型是(String) -> Unit,不是(Int) -> Unit
ints.myForEach(::shuChuInt)//这段语句是ok的,因为shuChuInt()方法对应的Lambda表达式的类型是(Int) -> Unit
var a2 = A2()
ints.myForEach(a2::shuChuInt_A)//获取A2类中方法shuChuInt_A的函数引用,并把它作为参数调用myForEach()方法
}
class A2{
fun shuChuInt_A(i:Int){ println(i)}//对应的Lambda表达式的类型是(Int) -> Unit
}
fun IntArray.myForEach(action:(Int) -> Unit){ for(i in this) action(i)}//参数必须是一个符合(Int) -> Unit类型的Lambda表达式
fun shuChuString(str:String){ println(str) }//对应的Lambda表达式的类型是(String) -> Unit
fun shuChuInt(i:Int){ println(i)}//对应的Lambda表达式的类型是(Int) -> Unit
“::”符号是取函数的引用符号,因为一个函数都对应这一个Lambda表达式类型,此处println()方法对应的表达式类型就是(Int) -> Unit,而笔者声明了IntArray的扩展方法myForEach()方法,这个方法的参数actions是一个类型为(Int) -> Unit的Lambda表达式
什么是高阶函数呢?就是以函数作为参数,又以函数作为返回值就是高阶函数,而每个函数又对应着Lambda表达式,说很多废话没有好处,我们来写几个实例来进行学习:
1.使用高阶函数对区间数组所有的元素进行求和
fun main(args: Array) {
var intRangerArray:Array = arrayOf(0..10,0..1000,0..10000)
println("区间数组总和为:"+intRangerArray.adds(IntRange::add))
}
fun Array.adds(action:(IntRange) -> Int ):Int{
var sum = 0
for(intRange in this){
sum += action(intRange)
}
return sum
}
fun IntRange.add():Int{
var sum = 0
for(i in this){
sum += i
}
return sum
}
2.使用高阶函数来过滤掉String数组,过滤掉数组的元素中存在某个子字符串的字符串
fun main(args: Array) {
var stringArray = arrayOf("ac","acd","asdas","asera5a5","s3s3s3","a5a5s3","s4fra5")
var guoLvStringArray = stringArray.guoLv2 (Array::guoLv1,"a5")//过滤子字符串为a5
for(i in 0 until guoLvStringArray.size){
println(guoLvStringArray[i])
}
}
fun Array.guoLv2(actions:(Array,String) -> Array,guolvString:String):Array{
return actions(this,guolvString)
}
fun Array.guoLv1(guoLvString: String):Array{
var size:Int = 0
for(i in 0 until this.size){
if(!this[i].contains(guoLvString)){
//没有包含过滤的子字符串
size++
}
}
var newStringArray = Array(size,{x:Int -> ""})
var index = 0
for(i in 0 until this.size){
if(!this[i].contains(guoLvString)){
//没有包含过滤的子字符串
newStringArray[index] = this[i]
index++
}
}
return newStringArray
}
当然这写代码对于Kotlin来说,没有Kotlin的风格,不够精简,其实有很精简的代码,只不过我写在这里超纲了,学完本篇文章,这里的代码你就知道如何精简了。
fun main(args: Array) {
var add1 = add(5)//把函数add返回的函数的引用赋值给add1
println(add1(2))//根据函数的引用add1来调用函数,调用的函数就是add()方法返回的函数
add1 = add(8)
println(add1(2))
}
fun add(a1:Int):(Int) -> Int{
return fun(a2:Int):Int{//返回一个函数
return a1+a2
}
}
输出:
7
10
函数式编程就是指函数可以作为参数来进行传递,然后有一些引用所记录,也就是说这些引用可以记录函数的状态,可以持有其运行权限,然后在合适的位置通过引用来调用这些被持有的状态的函数。
什么是函数的复合呢?高中时我们做的数学题里面经常会遇到复合函数,比如:f(x) = 3x+y,y = 3*f(x)+7,求x和y的值,这就是复合函数,我们拿一个简单的例子进行编程实现:
f(x) = 2x+3
f(y) = f(x)*2+6x,你能输出从x=0到100之间的对应的f(y)的值吗?代码如下:
fun main(args: Array) {
var intRange = 0..100//[0,100]
for(i in intRange){
print("x = ${i}时\n")
var fx = getFX(i)
print("f(x) = 2*${i} + 3 = ${fx}\n")
var fy = getFY(fx,i)
println("f(y) = 2*${fx} + 6*${i} = ${fy}\n")
}
}
fun getFX(x:Int) = 2*x + 3
fun getFY(fx:Int,x:Int) = 2*fx + 6*x
这一看,真心觉得代码有点繁琐啦!那么如何降低繁琐呢?Kotlin中的函数复合让复合问题变得如此简单,代码修改成函数复合模式如下所示:
fun main(args: Array) {
var intRange = 0..100//[0,100]
var fuHeGetFXFY = getFX andThen getFy //通过符号运算方法andThen得到复合函数fuHeGetFXFY
for(i in intRange){
println("当x = ${i}时,f(y) = ${fuHeGetFXFY(i)}")
}
}
var getFX = {x:Int -> 2*x +3}
var getFy = {fx:Int,x:Int -> 2*fx + 6*x}
infix fun Function1.andThen(fuction:Function2):Function1 {
return fun(p1:P1):R{
return fuction.invoke(this.invoke(p1),p1)
}
}//对Function1的方法进行扩展,扩展一个andThen符号运算的方法,看似这里代码很吓人,其实细细研究理解起来很简单,所以自己研究吧!锻炼能力的时候到了
科里化,什么意思啊!很懵逼啊!啥玩意,逗我了吧!
科里化就是把多个参数的方法变换成一系列单参数函数调用的变换,这个过程就是科里化。其实形象的理解就是把一个多参数调用结构函数分解为多个单参数调用结构的函数,比如如何把下列代码中的shuChu()方法科里化:
fun main(args: Array) {
shuChu("ellen",23,true)
}
fun shuChu(str:String,int:Int,boolean:Boolean){
println("输出字符串:${str}\n输出整形数值:${int}\n输出布尔值:${boolean}")
}
科里化后的代码:
fun main(args: Array) {
shuChu("ellen",23,true)//调用非科里化的方法
shuChu("ellen")(23)(true)//调用科里化后的方法
}
fun shuChu(str:String,int:Int,boolean:Boolean){
println("输出字符串:${str}\n输出整形数值:${int}\n输出布尔值:${boolean}")
}
fun shuChu(str:String)
= fun(int:Int)
= fun(boolean:Boolean)
= println("输出字符串:${str}\n输出整形数值:${int}\n输出布尔值:${boolean}")//这是科里化后的方法
你如果不想为每个函数写科里化代码,那么你应该为你的某些函数添加科里化工具,比如以下的代码:
fun main(args: Array) {
shuChu("ellen",23,true)//调用非科里化的方法
::shuChu.curruing()("ellen")(23)(true)//通过科里化工具进行科里化的调用
}
fun shuChu(str:String,int:Int,boolean:Boolean){
println("输出字符串:${str}\n输出整形数值:${int}\n输出布尔值:${boolean}")
}
fun Function3.curruing() = fun(p1:P1) =fun(p2:P2)= fun(p3:P3) = this.invoke(p1,p2,p3)//科里化工具,这里只能科里化三个参数的函数
什么是偏函数呢?就是得到一个科里化函数部分被调用的函数就是偏函数,比如我们上面科里化中代码如下所示:
fun main(args: Array) {
var pianHanhsu = ::shuChu.curruing()("ellen")(23)//通过科里化工具进行部分赋值,此时对应的函数就是偏函数
pianHanhsu(false)//调用偏函数pianHanShu
pianHanhsu(true)
}
fun shuChu(str:String,int:Int,boolean:Boolean){
println("输出字符串:${str}\n输出整形数值:${int}\n输出布尔值:${boolean}")
}
fun Function3.curruing() = fun(p1:P1) =fun(p2:P2)= fun(p3:P3) = this.invoke(p1,p2,p3)
那么偏函数有啥用途呢?我们通常要向文件当中写入数据,数据的编码格式是相当重要的,有些文件我使用”utf-8”格式写入,而有些文件呢?我们使用”GBk”格式写入,那么我们使用偏函数来模拟以下这个过程吧!懂了以下代码,你就会觉得Kotlin又一次显示出强大:
fun main(args: Array) {
var GBKWrite = ::writeDataToFile.curruing2()("GBK")
var UTF8Write = ::writeDataToFile.curruing2()("UTF-8")
GBKWrite("GBK数据")("GBK格式文件1.gbk")//GBk格式数据的写入
UTF8Write("UTF-8数据")("UTF-8格式文件.utf8")//UTF-8格式数据的写入
}
fun writeDataToFile(data:String,geShi:String,fileName:String){
println("写入数据\"${data}\"到文件\"${fileName}\"成功!编码格式为:${geShi}")
}
fun Function3.curruing2() = fun(p2:P2) =fun(p1:P1)= fun(p3:P3) = this.invoke(p1,p2,p3)
输出:
写入数据”GBK数据”到文件”GBK格式文件1.gbk”成功!编码格式为:GBK
写入数据”UTF-8数据”到文件”UTF-8格式文件.utf8”成功!编码格式为:UTF-8
fun main(args: Array) {
//这是Kotlin中的双斜杠注释,作用于一行
/*
这是Kotlin中的多行注释,作用与多行
*/
//最后,只想说句,这跟Java当中的注释是一样一样的
}
一个字符串要么里面完全包含纯字符,要么里面镶嵌有表达式或者变量以及转义字符,当有表达式和变量的时候呢,需要使用关键字”${表达式或者变量}”进行声明,示例代码如下:
fun main(args: Array) {
var str1 = "adssadasdsada"; // 这是纯字符串
var str2 = "11111${str1}22222" //这是镶嵌变量的字符串
var str3 = "11111${str2+"123123123131312"}+str" // 这是镶嵌表达式的字符串
var str = "111111\"2222222222\"" //这是镶嵌有转义字符的字符串
}
这里笔者只贴出代码,要看懂还得自行研究,能力的体现在于此刻!
将某个数组公式化到另外一个数组中:
fun main(args: Array) {
var intArray = intArrayOf(1,2,3,4,5)
var intArray2 = IntArray(intArray.size)//公式化后的数组
var index = 0
intArray.forEach {
var i = it* 2 + 3//公式化
intArray2[index] = i
index++
}
intArray2.forEach(::println)//遍历公式化数组并且输出
}
把一个Int数组转化为Double数组
fun main(args: Array) {
var intArray = intArrayOf(1,2,3,4,5)
var doubleArray = DoubleArray(intArray.size)
var index = 0
intArray.map{
doubleArray[index] = it.toDouble()
index++
}
doubleArray.forEach(::println)
}
把一个区间数组进行压扁处理
fun main(args: Array) {
var intRangeArray = arrayOf(0..20,1..50,2..100)
var numberSize = 0
intRangeArray.forEach {//得到扁平后对应的Int数组的大小
numberSize += it.last - it.first +1
}
var intArray = IntArray(numberSize)
var index = 0
intRangeArray.flatMap {
it.map {
intArray[index] = it
index++
}
}
intArray.forEach(::println)
}
对一个区间数组进行扁平化求和
fun main(args: Array) {
var intRangeArray = arrayOf(1..100,101..500,501..700,701..900,901..1000)
val intRangeMap = intRangeArray.flatMap{ it }
println(intRangeMap.reduce{act,i -> act+i})//求累和
println(intRangeMap.reduce{act,i -> act*i})//求累乘
}
将一个数组进行公式化,并且将其运算过程转化为字符串,并输出 ,公式为x*2+3
fun main(args: Array) {
var intArray = intArrayOf(1,2,3,4,5)
println(intArray.map{ it*2+3 }.fold(StringBuffer()){acc, i -> acc.append("${(i-3)/2}*2 +3 = ${i}\n")})
}
将一个Int数组进行过滤,保留其中的偶数
fun main(args: Array) {
var intArray = arrayOf(1,2,3,4,5,6,7,8,9,10)
var intArray2 = intArray.filter { it%2 ==0 }
intArray2.forEach(::println)
}
将一个Int数组进行从头到尾的遍历子数组截取,直到发现子元素能够被8整除就结束截取
fun main(args: Array) {
var intArray = arrayOf(1,2,3,4,5,6,7,8,9,10)
var intArray2 = intArray.takeWhile { it%8 !=0 }
intArray2.forEach(::println)
}
输出:
1
2
3
4
5
6
7
如何在一个引用判断为空的时候执行多条语句,注意只能把这个引用判断一次
fun main(args: Array) {
var student = backStudnet()
student?.let {
println(it.name)
println(it.age)
}
student?.apply {//apply也可以办到
println(this.age)
println(this.name)
}
}
fun backStudnet():Student?{
return null
}
class Student(var name:String,var age:Int)
在Java当中如果外层循环要结束掉整个循环,代码会很复杂,但是Kotlin却很简单easy,使用标签来进行结束,稳准狠,示例代码如下:
for1@ for (i in 0..10){
for (j in 0..10){
if(i == 10&&j == 0)
break@for1//当 i=10,j = 0的时候结束掉整个循环
if(i == 9&&j == 9)
continue@for1//当 i = 9,j = 9的时候结束掉外层for语句的当前一次循环
println("i=${i}时,j = ${j}")
}
}
标签处返回,当我们在一个函数里面执行循环,循环里面呢执行了return语句,那么意味着这整个函数就被执行完了,但是有些时候,我们的函数的循环里面有Lambda表达式,我们希望我们return语句返回的是Lambda表达式,而不是整个函数,示例代码如下:
fun method(){
var intArray = intArrayOf(1,2,3,4,5,6)
intArray.forEach {
if(it % 5 == 0) return
//很明显这里结束的是整个method()方法,我们不想结束整个method方法,而是仅仅结束forEach()方法
println(it)
}
}
解决方法,还是使用标签:
fun method():IntArray{
var intArray = intArrayOf(1,2,3,4,5,6)
intArray.forEach return1@ {
if(it % 5 == 0) return intArray //这里结束整个method()方法
else return@return1 //这里结束forEach方法
println(it)
}
return intArray.filter { it%2 == 0 }.toIntArray()
}