kotlin vararg 、infix、tailrec

1.官方文档

  https://kotlinlang.org/docs/functions.html#variable-number-of-arguments-varargs

  https://www.kotlincn.net/docs/reference/functions.html#%E5%8F%AF%E5%8F%98%E6%95%B0%E9%87%8F%E7%9A%84%E5%8F%82%E6%95%B0varargs

2.vararg

2.1 作用与调用方法

  kotlin 中的函数支持可变参数,用vararg关键字声明。它生成的实际上是一个同类型的数组参数。在函数内可以数组形式使用它,如下:

1 fun fun1(vararg numbers : Int){
2     val sz = numbers.size
3     for (i in 0 until sz){
4         Log.e(TAG_VARARG,"fun1 bumber[$i] = ${numbers[i]}")
5     }
6     for (num in numbers){
7         Log.e(TAG_VARARG,"fun1 $num")
8     }
9 }

调用方法,实参个数可以为0

1     //1.简单调用
2     fun1(1,2,3,4,5)
3     fun1()  //变参可以个数为0

使用*加数组名调用

1     //2.使用*加数组名调用
2     val array = intArrayOf(6,7,8,9,0)
3     fun1(*array)
4 
5     val array2 = ArrayList<Int>()
6     fun1(*array2.toIntArray())

无效的调用

1     //3.无效调用
2     val array3 = ArrayList<Int>()
3     val f1 = 3.14f;
4     val i2 = 100
5     fun1(*f1) //error ,
6     fun1(*i2) //error
7     fun1(*array3) //error,ArrayList不是数组 

2.2 可变参数的个数只能是一个

下面是错误的声明,有两个vararg参数

1 fun fun2(vararg args : Int,vararg args2 : Int){
2 
3 }

2.3 变参与数组参数的对比

在顶级定义相同类型的变参与数组参数,它们生成的函数签名相同

 1 fun fun3(vararg numbers : Int){
 2 }
 3 fun fun3(numbers : IntArray){
 4 }
 5 class JJJJJJ{
 6     fun fun33(vararg numbers : Int){
 7     }
 8     fun fun33(numbers : IntArray){
 9     }
10 }

  两个fun3冲突,两个fun33也冲突,报错如下:

  

局部范围内定义相同类型的变参与数组可共存 

 1 //3.2 局部范围内定义相同类型的变参与数组可共存
 2 fun vararg_test3_2(){
 3     fun fun3(vararg numbers : Int){
 4         Log.e(TAG_VARARG,"fun3(vararg)  ${numbers[0]}")
 5     }
 6     fun fun3(numbers : IntArray){
 7         Log.e(TAG_VARARG,"fun3(IntArray)  ${numbers[0]}")
 8     }
 9     fun fun3(numbers : Array<Int>){
10         Log.e(TAG_VARARG,"fun3(Array<Int>)  ${numbers[0]}")
11     }
12     val array1 = intArrayOf(1,2,3,4)
13     val array2 = arrayOf(5,6,7,8)
14     fun3(0)        //调用的是fun3(vararg)
15     fun3(array1)        //调用的是fun3(IntArray)
16     fun3(array2)        //调用的是fun3(Array<Int>)
17 }

数组参数只能传数组对象

 1 //3.3 数组参数只能传数组对象
 2 fun vararg_test3_3(){
 3     fun fun3(numbers : IntArray)  {
 4     }
 5     fun fun3(vararg numbers : Int)  {
 6     }
 7     val array = intArrayOf(1,2,3)
 8 //    fun3(1,1f)            //error,只能传数组参数
 9     fun3(array)
10     fun3(1)     //可以传类型
11 }

数组对象可返回,变参不可返回

1 //3.4 数组对象可返回,变参不可返回
2 fun fun3_4() : IntArray? = null
3 //fun fun3_4(a:Int) : (vararg arg : Int)? = null  //error

2.4 可变参数的位置:前,中、后

  当可变参数不是最后一个时,其它参数要显示指定才可以

 1 //4.可变参数的位置:前,中、后,当可变参数不是最后一个时,其它参数要显示指定才可以
 2 fun fun4(vararg args : Int,name : String,i : Int = 0) = Unit
 3 fun fun4(i : Int = 1,vararg args : Int,name : String) = Unit
 4 fun fun4(name : String,i : Int = 2,vararg args : Int) = Unit
 5 
 6 fun vararg_test4(){
 7     fun4(1,name = "",i = 99)       //调用第1个
 8     fun4(1,name = "")              //调用第1个
 9 
10     fun4(2,3,name = "")           //调用第2个
11     fun4(2,name = "")              //调用第2个
12 
13     fun4(name = "",i = 10,2,3,4)   //调用第3个
14     fun4("",2,5,6,7)           //调用第3个
15 }

2.5 lambda的参数不支持可变参数

1 //5.lambda的参数不支持可变参数
2 //fun fun5(vararg lambdas : (vararg lmd2 : Int) -> Int)){}   //error
3 //fun fun5(num : Int, lmd : (vararg lmd2 : Int) -> Int){}   //error
4 fun vararg_test5() = Unit

2.6 可变参数的类型是函数或lamuda表达式

 1 //6.可变参数的类型是函数或lamuda表达式
 2 fun fun6(vararg lambdas : (a : Int,b : Int) -> Int) : Int{
 3     var total = 0
 4     for (i in 0 until lambdas.size){
 5         val lambda = lambdas[i]
 6         total += lambda(i,10)
 7     }
 8     return total
 9 }
10 fun add(a : Int,b : Int) = a + b
11 fun vararg_test6(){
12     val sum1 = fun6(::add,::add,::add,::add)
13     Log.e(TAG_VARARG,"sum1 = $sum1")
14 
15     val sum2 = fun6({x,b-> 3 }, ::add,::add)
16     Log.e(TAG_VARARG,"sum2 = $sum2")
17 }

结果

2021-03-15 23:08:42.526 16625-16625/com.example.kotlin E/vararg: sum1 = 46
2021-03-15 23:08:42.527 16625-16625/com.example.kotlin E/vararg: sum2 = 26

2.7 变参与模板

1 //7.模板
2 fun<T,U> fun7(u : U,vararg args : T) = Unit
3 fun vararg_test7(){
4     fun7(1,1,2,3,4,)
5 }

2.8 变参的默认值

变参可以有默认值

 1 //8.变参可以有默认值
 2 fun fun8(vararg args : Float = FloatArray(8)){
 3     Log.e(TAG_VARARG,"args.size = ${args.size}")
 4 }
 5 fun vararg_test8(){
 6     fun8()                  //结果 8
 7     fun8(1f,2f)          //结果 2
 8     val floatArray = FloatArray(4,)
 9     fun8(*floatArray,)      //结果4
10 }

结果

2021-03-15 23:13:44.615 17263-17263/com.example.kotlin E/vararg: args.size = 8
2021-03-15 23:13:44.615 17263-17263/com.example.kotlin E/vararg: args.size = 2
2021-03-15 23:13:44.615 17263-17263/com.example.kotlin E/vararg: args.size = 4

3.infix

3.1 简介

  标有 infix 关键字的成员函数或者扩展函数 可以忽略该调用的点和圆括号。如

   infix fun Int.suffix(subfix : String) = "$this-$subfix"

  可以这个函数的调用可以写成:

  99 suffix "sss"

  99 是 Int , suffix是函数, "sss" 是参数

3.2 infix的要求

  • 必须是成员函数或扩展函数
  • 函数只能有一个参数
  • 函数的形参不能是变参
  • 函数的形参不能有默认值
    infix fun Int.Max(max : Int = 64) = max  //error
  • 在使用infix语法调用时必需指定接收者,如下第8行是个错误。
     1 //指定接收者
     2 fun infix_test1_2(){
     3     class JJJ {
     4         infix fun prefix(prefix : String) = "$prefix-$this"
     5         fun test(){
     6             prefix("EE")           //ok
     7             this prefix "HHH"       //ok
     8             //prefix "jjj"          //error
     9         }
    10     }
    11 }

3.3 infix函数的优先级

它的优先级在  ..  之后, 在  ?: 之前,具体如下表:

PrecedenceTitleSymbols
Highest Postfix ++--.?.?
  Prefix -+++--!label
  Type RHS :asas?
  Multiplicative */%
  Additive +-
  Range ..
  Infix function simpleIdentifier
  Elvis ?:
  Named checks in!inis!is
  Comparison <><=>=
  Equality ==!==
  Conjunction &&
  Disjunction ||
  Spread operator *
Lowest Assignment =+=-=*=/=%=

示例:

 1 //2.它的优先级在  ..  之后, 在  ?: 之前
 2 fun infix_test2(){
 3 
 4     infix fun Int.suffix(subfix : String) = "$this-$subfix"
 5     infix fun Int.MAX(max : Int ) = max
 6 
 7     val str1 = 1.suffix("sss")
 8 //    val str2 = "eee" + 2 suffix "aaa"  //error
 9     val str3 = "eee-" + (3 suffix "aaa")  //ok
10     val str4 = null ?: 4 suffix "bbb"
11     var str5 = ""
12     for (i in 0 until (3 MAX 6)){
13         str5 += "$i"
14     }
15 
16     Log.e(TAG_INFIX,"str1 = $str1")
17     Log.e(TAG_INFIX,"str2 = error")
18     Log.e(TAG_INFIX,"str3 = $str3")
19     Log.e(TAG_INFIX,"str4 = $str4")
20     Log.e(TAG_INFIX,"str5 = $str5")
21 }

结果

1 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str1 = 1-sss
2 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str2 = error
3 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str3 = eee-3-aaa
4 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str4 = 4-bbb
5 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str5 = 012345

4.tailrec

4.1 功能

  在 Kotlin for JVM 与 Kotlin/Native 中支持尾递归,在满足尾递归条件的函数前面加tailrec修饰,编译器会优化该递归成一个快速而高效的基于循环的版本,减少栈消耗。

  不加tailrec不优化。

4.2 尾递归条件

  • 最后一条语句是函数调用语句
  • 调用的函数是自身

错误示例1:

1 fun factorial1(n : Int) : Int{
2     return if (n == 1) 1 else n * factorial1(n - 1)
3 }

  最后一句代码是 n *  factorial1(n - 1) ,这是个表达式,不是函数调用,虽然这个表达式是包含了一个函数调用。

错误示例2:

 1 tailrec fun btree(root : TreeNode?) {
 2     if (root?.left != null) {
 3         btree(root.left)
 4     }
 5     println("value = ${root?.value}")
 6     if (root?.right != null){
 7         btree(root.right)
 8         println()
 9     }
10 }

  对自身的调用不是最后一条语句。

4.3 示例 

 1 //2.示例
 2 fun factorial1(n : Int) : Int{
 3     return if (n == 1) 1 else n * factorial1(n - 1)
 4 }
 5 
 6 var ret2 = 1
 7 tailrec fun factorial2(n : Int) : Int {
 8     if (n <= 1) return 1
 9     ret2 *= n
10     return factorial2(n - 1)
11 }
12 
13 var ret3 = 1
14 fun factorial3(n : Int) : Int {
15     if (n <= 1) return 1
16     ret3 *= n
17     return factorial3(n - 1)
18 }
19 
20 fun tailrec_test(){
21     var start = SystemClock.elapsedRealtimeNanos()
22     val ret1 = factorial1(6)
23     var end = SystemClock.elapsedRealtimeNanos()
24     Log.e(TAG_TAILREC,"ret1 = $ret1 ,takes ${end - start}")
25 
26 
27     start = SystemClock.elapsedRealtimeNanos()
28     factorial2(6)
29     end = SystemClock.elapsedRealtimeNanos()
30     Log.e(TAG_TAILREC,"ret2 = $ret2 ,takes ${end - start}")
31 
32     start = SystemClock.elapsedRealtimeNanos()
33     factorial3(6)
34     end = SystemClock.elapsedRealtimeNanos()
35     Log.e(TAG_TAILREC,"ret3 = $ret3 ,takes ${end - start}")
36 
37 }

结果:

2021-03-16 21:41:16.753 19747-19747/com.example.kotlin E/tailrec: ret1 = 720 ,takes 17810
2021-03-16 21:41:16.753 19747-19747/com.example.kotlin E/tailrec: ret2 = 720 ,takes 16930
2021-03-16 21:41:16.753 19747-19747/com.example.kotlin E/tailrec: ret3 = 720 ,takes 18800
  • factorial1 是非尾递归、
  • factorial2是尾递归、加了tailrec的,启用优化
  • factorial2是尾递归、没加tailrec,未优化

它们的Kotlin ByteCode对比如下:

factorial1

 1   public final static factorial1(I)I
 2     // annotable parameter count: 1 (visible)
 3     // annotable parameter count: 1 (invisible)
 4    L0
 5     LINENUMBER 32 L0
 6     ILOAD 0
 7     ICONST_1
 8     IF_ICMPNE L1
 9     ICONST_1
10     GOTO L2
11    L1
12     ILOAD 0
13     ILOAD 0
14     ICONST_1
15     ISUB
16     INVOKESTATIC com/example/kotlin/TailrecKt.factorial1 (I)I
17     IMUL
18    L2
19     IRETURN
20    L3
21     LOCALVARIABLE n I L0 L3 0
22     MAXSTACK = 3
23     MAXLOCALS = 1

factorial2

 1   // access flags 0x19
 2   public final static factorial2(I)I
 3     // annotable parameter count: 1 (visible)
 4     // annotable parameter count: 1 (invisible)
 5    L0
 6     LINENUMBER 37 L0
 7     ILOAD 0
 8     ICONST_1
 9     IF_ICMPGT L1
10     ICONST_1
11     IRETURN
12    L1
13     LINENUMBER 38 L1
14     GETSTATIC com/example/kotlin/TailrecKt.ret2 : I
15     ILOAD 0
16     IMUL
17     PUTSTATIC com/example/kotlin/TailrecKt.ret2 : I
18    L2
19     LINENUMBER 39 L2
20     ILOAD 0
21     ICONST_1
22     ISUB
23     ISTORE 0
24     GOTO L0
25    L3
26     LOCALVARIABLE n I L0 L3 0
27     MAXSTACK = 2
28     MAXLOCALS = 1

factorial3

 1   public final static factorial3(I)I
 2     // annotable parameter count: 1 (visible)
 3     // annotable parameter count: 1 (invisible)
 4    L0
 5     LINENUMBER 44 L0
 6     ILOAD 0
 7     ICONST_1
 8     IF_ICMPGT L1
 9     ICONST_1
10     IRETURN
11    L1
12     LINENUMBER 45 L1
13     GETSTATIC com/example/kotlin/TailrecKt.ret3 : I
14     ILOAD 0
15     IMUL
16     PUTSTATIC com/example/kotlin/TailrecKt.ret3 : I
17    L2
18     LINENUMBER 46 L2
19     ILOAD 0
20     ICONST_1
21     ISUB
22     INVOKESTATIC com/example/kotlin/TailrecKt.factorial3 (I)I
23     IRETURN
24    L3
25     LOCALVARIABLE n I L0 L3 0
26     MAXSTACK = 2
27     MAXLOCALS = 1

 

posted @ 2021-03-15 18:15  f9q  阅读(754)  评论(0编辑  收藏  举报