Kotlin语法基础,控制流

Posted 北漂周

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin语法基础,控制流相关的知识,希望对你有一定的参考价值。

控制流

通常,计算机程序编写出来,就是为了让计算机理解而解决某问题。按照预先设定的顺序执行的指令序列,也就显得尤为重要。程序员除了学会告诉计算机需要处理的那些数据以外,还需要学会指定这些指令的执行顺序,在程序机构中,我们称之为控制流。

控制流,也成为控制结构,通常包括:顺序结构、条件结构、循环结构、转向结构。

顺序结构是组成计算机程序的基本结构,它依据指令序列的先后顺序,从上到下依次执行。

顺序结构是组成计算机程序的基本机构,它依据指令序列的先后顺序,从上到下依次执行。

下面我们先来了解条件结构,条件结构又称分之结构,在这种结构中包含一个条件判断,根据条件是否成立来确定是执行A分支还是执行B分支。

条件结构(if、when)

Kotlin语言提供两种分支语句,“if”“when”。通常,当条件判断简单且可能的情况很少时,使用if语句。而条件分支较多的情况时,建议搭建使用when语句。

if 语句

作为程序语句最基本的控制结构,if语句分为三种基本结构:单分支结构、双分支结构、多分支结构。

  • if单分支结构
    if的单分支结构是最简单的if结构,只包含一个条件选择,当且仅当条件表达式为真时,才执行相关代码,且流程图和标准格式如下:

if <条件表达式> 
    语句体

举个例子:

val intA : Int = 3

// 如果intA的值大于1,则执行下面的语句
if (intA > 1) 
    println("intA is " + intA)
  • if双分支选择结构
    双分支选择结构是单分支结构的升级版,我们可以使用if-else构成双分支结构,它和但分支结构的区别在于条件表达式为假时,会执行else分支的语句体,其流程如下:

if <条件表达式> 
    // 如果条件表达式为真则执行语句体1
    语句体1
 else 
    // 如果条件表达式为假则执行语句体2
    语句体2

举个例子:

val intB : Int = 3

if (intB > 1) 
    // 如果intB的值大于1,则执行下面的语句
    println("intB is bigger than 1")
 else 
    // 如果intB的值小于1,则执行下面的语句
    println("intB is smaller than 1")

在简单的双分支结构中,确保了至少一种分支会被执行,就在二选一的场景中是非常实用的。

  • if多分支选择结构
    多分支选择结构用于条件分支大于两个的情况,我们也可以把多个if语句连载一起,从而构成if的多分支结构,形成下面这样的形式:

if <条件表达式1> 
    // 如果条件表达式1为真,则执行语句体1
    语句体1
 else if <条件表达式2>
    // 如果条件表达式1为假,且表达式2位真,则执行语句体2
    语句体2
 else 
    // 如果条件表达式1为假,且表达式2位假,则执行语句体3
    语句体3

当条件表达式1的值为真的时候,将执行语句体1,。当它为家的时候,则转向判断条件表达式2的值是否为真。如果条件表达式2为真,则执行语句体2,它不为真则转向下一个判断分之。如此循环进行判断,如果所有的分之都没有满足条件,则执行最后一个else块内的语句体。如果最后没有else,则不执行任何分之语句。

when 语句

虽然if-else可以构成多分支结构,但是需要判断的条件分支过多的时候,继续使用if-else使得代码读起来冗长而逻辑层次过多,所有我们建议大家在类似的这种情况下使用when语句。
在Kotlin语言之中,没有switch语句,取而代之的是when语句。其逻辑结构如下:

when <value> 
    <表达式1> -> 语句体1 // 表达式1为真,则执行语句体1
    <表达式2> -> 语句体2 // 表达式2为真,则执行语句体2
    <表达式3> -> 语句体3 // 表达式3为真,则执行语句体3
    <表达式4> -> 语句体4 // 表达式4为真,则执行语句体4
    ......
    else -> 默认语句体  // else可以不写。如果存在else,所有条件均为假,则执行默认语句体

值得注意的是:Kotlin语言中when语句并不仅仅是针对Java语言switch语句的替代,when语句在使用上还进行了扩展升级。常用的形式有三种:常量结构、in结构、表达式结构。

when语句与switch语句的区别,如果符合判断中的任意一条表达式,则在执行表达式对应的语句体之后,直接跳出整个when逻辑,我们不需要手动的break。

>

  • 常量结构

常量结构与我们熟知的switch使用方式很类似,之间判断value的值是否是在所列举的表达式之中,如果存在,则执行对应的语句体。

val intA : Int = 1
when (intA) 
    1 -> println("intA == 1") // 打印这句话
    2 -> println("intA == 2")
    else -> 
        println("intA is neither 1 nor 2")
    

when 将它的参数和所有的分支条件,从上到下顺序比较,直到某个分支满足条件为止。遇到满足条件的分支,则执行对应的语句。如,这里intA的值就是1,满足了第一个分支逻辑,所以执行了 println(“intA == 1”) 语句。

  • in结构

这里再次强调一下,针对when而言,它没有类似switch的break操作。如果我们希望一个逻辑值value能够对应多个常量值,则我们就必须使用in结构。其操作,与常量结构的when语句类似,即将满足一个条件的多个常量值使用 “,” 隔开,如:

val intB : Int = 3
when (intB) 
    1,2,3,4 -> println("intB in 1,2,3,4") // 仅执行这一句
    3,4,5 -> println("intB in 3,4,5") // 这句话不会被打印
    else -> 
        println("intB is not in 1,2,3,4,5")
    
  • 表达式结构

常量的限制还是比较多,when还给提供用任意表达式(而不只是常量)作为分支条件。

when (obj) 
    1 -> 
        println("obj is a number , value = 1")
    
    "hello" -> 
        println("obj is a String 'hello'")
    
    is Long -> 
        println("obj's type is Long")
    
    else -> println("Unknown")

当然,我们也可以将多个表达式对一个判断同时使用,只需要在两个表达式之间增加“,”符号。如,这里我们有一个需求,判断一个数字是整数还是浮点数,使用when语句的操作就如下所示:

when (obj) 
    is Int, is Long, is Short, is Byte -> 
        // obj 是一个整数型数字
        println("obj is a integer number")
    
    is Float, is Double -> 
        // obj 是一个浮点型数字
        println("obj is a float number")
    
    else -> 
        // obj 不是数字
        print("obj is not a number")
    

Kotlin语言中的is运算符,是用来在运行时指出对象是否是特定类的一个实例,类似Java语言中的instaceof。

对于when而言,在其语句体之中,它本身是包含值返回的。即,我们书写在语句中的内容,能够直接作为when的返回值。如:

fun hasPrefix(x: Any) = when(x) 
    is String -> x.startsWith("prefix")
    else -> false

when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:

when 
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")

循环机构(for-in、while)

循环结构可以理解为重复迭代结构,在条件满足的情况下, 会重复执行循环体中的代码指令,知道条件不满足位置。在Kotlin中循环结构使用关键字for-in、while、do-while来构成。

  • for-in循环

用来遍历一个范围(range)、队列(sequence)、集合(collection)、数组(array)里面的所有元素,并且执行一段语句体代码。其中,被反复执行的程序段称之为循环体,用来控制循环进行迭代的变量成为循环变量。在for-in循环中,循环变量可以使一个被隐式声明的,只需要在每次循环遍历开始时被自动复制的变量,它包含在循环的声明中,而不需要特意使用显示的声明。for-in循环的标准格式和流程图如下:

for (<循环变量> in <范围、集合、数组...>) 
    循环体...

循环变量只存在当前循环的声明周期之中,如果你想在循环完成后访问循环变量,你必须在循环声明之前单独进行声明。

如这里我们声明了一个员工的array,employees,我们希望遍历所有的员工姓名,我们就可以这样做:

val employees = arrayOf("david", "mike", "jason")
for (emp in employees) 
    println("employee name = $emp")

输出:

employee name = david
employee name = mike
employee name = jason

for-in语法逻辑类似于Java语言之中的foreach。语句用于循环访问集合以获取所需信息,但不应用于更改集合内容以避免产生不可预知的副作用。

当然,在Kotlin语言中也提供了在withIndex的函数,让我们方便去拿到每个对象的下标。如:

// 使用array 的 withIndex , 可以获取数组之中的下标index
for ((index, emp) in employees.withIndex()) 
    println("the $index employee name is $emp")

如果只想打印下标,则直接使用indices属性即可。

// 仅打印下标
for (index in employees.indices) 
    println("$index")

对于字符串而言,字符串本身就是一个字符数组,我们同样可以使用for-in的方式,将其中每一个字符打印出来。

// 循环打印显示每一个字母
for (word in "hello world") 
    print(" $word")

即得到结果:

h e l l o   w o r l d
  • while循环

while循环用于实现“当型”循环结构,即只有当条件满足时才执行循环体,Kotlin语言中提供两种while的循环方式:while、do-while。两种方式很类似,这里我们先介绍while循环。

每次在循环开始时计算表达式,如果表达式值为true,会执行循环体。执行完循环体之后再进行表达式的判断,知道条件表位false才终止循环。while循环比较适合在循环开始前并不知道循环次数的情况。while循环的流程图和标准结构如下:

while (<表达式>) 
    循环体

如这里我们做一个从10到1的倒数计时,使用while循环我们就可以这么干:

var countDown = 10

// 当countDown大于0的时候,执行循环体中的内容
while (countDown > 0) 
    println("current number $countDown")
    countDown--

while循环特别容易造成死循环,必须记住判断每次循环体的条件改变,造成表达式在一定的情况下能够为假,终止while循环。

  • do-while循环

do-while循环和while循环极为类似,它和while循环的区别在于,当执行到这一个循环结构时,会限制性循环体的代码,在判断循环条件,确定是否继续循环执行。知道条件表达式为false。也就是说循环体逻辑至少会被执行一次。do-while的循环流程如下:

do 
    循环体
 while (<条件表达式>)

这里我们将上述while的逻辑做一个简单的修改,如下所示:

countDown = 0
do 
    // 此处逻辑必定会执行一次
    println("current number $countDown")
    countDown--
 while (countDown > 0)

我们会发现,无论countDown > 0 条件成立与否,循环体之中的内容都会执行一次,即先执行后判断。

控制转向语句(continue、break、return)

控制专项语句是指当程序执行到改语句时,立即转移到程序的其他位置执行。也就是改变代码的执行顺序,通过它可以实现代码的跳转,Kotlin有三种控制转向语句,continue、break、return:

continue

continue,即继续的意思。continue会通知一个循环体立即停止本次循环,直到回到循环判断条件,重新开始下次循环。我们这里我们来举一个例子:

// continue
val words = "abcdefg"
for (w in words) 
    // 遇到c字符,直接执行下一次循环条件判断
    if (w == 'c') 
        continue
    
    print("$w-")

运行后我们得到数据:

a-b-d-e-f-g-

我们能够看到,当for循环答应字符串”abcdefg” 的时候,遇到’c’字符,我们就有选择性的跳过了。当然,continue不仅仅能够与for循环配合,它与while、do-while循环配合能够产生一样的效果,如:

var countDownC = 10
do 
    countDownC--
    // 遇到数字5,直接执行下一次循环条件判断
    if (countDownC == 5) 
        continue
    
    print("$countDownC-")
 while (countDownC > 0)

运行后得到:

10-9-8-7-6-

从上述例子我们可以看得出来,continue的主要作用是与循环逻辑配合来使用。很多情况下,我们在一些循环逻辑之中需要批量处理一系列数据,其中又想忽略某些数据不出来,continue的优势就显而易见了。

break

break语句会强制结束整个控制结构的执行,你可以在循环体之中使用它。当一个循环体之执行到break时,会立即终端该循环体,然后跳转到表示循环体结束的位置,跳出循环层。

如,这里我们有一个需求,需要从所有的学生之中寻找到一个名叫“jason”的学生,并返回其所在的位置。我们就可以这样写:

val students = arrayOf("mike", "david", "jason", "green", "eva")

for ((index, stu) in students.withIndex()) 
    // 寻找jason的下标
    if (stu == "jason") 
        println("find jason,his index is $index")
        break // 跳出for
     else 
        println("find student $stu , find next")
    

使用break,我们能够在找到jason之后跳出整个for循环,程序不会继续对students进行遍历,从而一定上节约了计算开销。
如同continue一样,break同样能够在while与do-while循环中使用,直接结束整个循环过程,如:

var countDownD = 10

while (countDownD > 0) 
    countDownD--
    println("countDownD is $countDownD")
    if (countDownD == 5) 
        break // countDownD为5时,跳出while
    

这里需要注意的是,break只能够写在循环体之中,如果存在多重嵌套循环,break结束的循环是最近的循环。如,这里我们有一个需求,为了查询班里是否存在有男学生的名字与女学生的名字相同,操作如下:

// 判断是否存在同名的男女学生

// 男学生
val maleStudent = arrayOf("david","jason","mike")
// 女学生
val femaleStudent = arrayOf("marry","mike","eva")

// 标记是否找到
var hasFind = false

for (male in maleStudent) 
    for (female in femaleStudent) 
        if(male == female) 
            hasFind = true // 做个标记符,不然外层循环不知道内层循环是否找到,无法判断是否需要break
            println("find the same name student!")
            break // 跳出女学生for循环
        
    
    if (hasFind) 
        break // 跳出男学生for循环
    

通常,多重循环希望使用break作为跳出处理,都会增加一个标识符,如上述例子标识符就是hasFind。在外层循环的判断跳出逻辑则是根据标识符来做break与否的操作。

当然,对于跳出多重循环的方式,不一定都需要加标志,我们还可以使用标签的方式解决。后面,我们会跟大家介绍。

return

return意为返回,return关键字是配合着函数(fun)而是用的,我们尝试用的main就是一个主入口函数,函数(fun)的后面会有具体说明,在Kotlin语言中,针对函数的返回值而言是非常灵活的,在需要的时候,你可以定义一个或者选择性省略返回值。

在定义一个函数的返回值的时候,我们可以使用我们常见的基础类型,当然如果该函数没有任何的返回,也可以不写,即认为是无返回值。

/**
 * 返回a+b的值
 */
fun add(a: Int, b: Int): Int 
    return a + b // 向函数体返回a+b的值


/**
 * 查找x字符串
 */
fun findX(xArray : Array<String>) 
    for (word in xArray) 
        if (word == "x") 
            println("find the 'x' word!")
            return // 中断整个循环执行,退出函数
        
    
    println("Can not find 'x' !")


fun main(args: Array<String>) 

    val result = add(1,2)
    println("a + b = $result")


    val xArray = arrayOf("a","b","x","c","d")
    findX(xArray)

标签(@)

在Java语言之中,你可以调用break和continue跳出本次循环或者本层循环,这种控制方法在循环嵌套场景中非常使用。不过,Kotlin语言提供了更强大的跳出机制,你可以显示地跳出需要跳出的是哪一层循环。

为了实现这么一个目的,我们可以使用标签来,为循环体或者代码块打上标记,需要使用break或者continue时,带上这个标签就可以控制该标签代表处的对象的中断或者跳出。

标签必须是要伴随着continue、break、return三个控制转向语句一起使用的,

<标签名>@ (循环体|函数) continue|break|return@<标签名>

如上述我们查找同名男女学生的例子,我们使用标签之后,我们的break就可以写成如下所示:

  • 配合break使用
// 男学生
val maleStudent = arrayOf("david","jason","mike")
// 女学生
val femaleStudent = arrayOf("marry","mike","eva")


// 标签配合break使用
formale@ for (male in maleStudent) 
    for (female in femaleStudent) 
        if(male == female) 
            println("find the same name student!")
            break@formale // 从标签@formale处跳出
        
    
  • 配合continue使用
// 标签配合continue使用
formale@ for (male in maleStudent) 
    for (female in femaleStudent) 
        if(male == female) 
            println("find the same name student!")
            continue@formale // 继续查找下一个
        
    
  • 配合return使用

正如我们上述所说,return是配合着函数(fun)而使用的。而对于Kotlin语言来说,单纯的fun是无法完成嵌套的。所以,很多情况下标签配合着return使用都是在lambda 表达式和匿名内部类中完成。

/**
 * return 标签测试
 */
fun foo() 
    val ints = arrayOf(1,2,0,3,5)

    ints.forEach lit@ 
        if (it == 0) return@lit
        print(it)
    


/**
 * 匿名内部类 return 标签配合测试
 */
fun foo2() 
    val ints = arrayOf(1,2,0,3,5)
    ints.forEach(fun(value: Int) 
        if (value == 0) return
        print(value)
    )

以上是关于Kotlin语法基础,控制流的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin语法基础,控制流

Python-11:Python语法基础-控制流

Kotlin基础控制流和函数

Kotlin循环控制

go语言学习笔记 — 基础 — 控制流:goto跳转语句

Kotlin学习笔记——归纳整理