3.你不知道的go语言控制语句
阅读原文时间:2023年08月20日阅读:1

目录

本篇前瞻

好的,现在你已经来到一个新的小结,在这里你将学习到go语言的重要内容,习得go 25个关键字中的12个:var, const, if, else, switch, case, default, fallthrough, for, break, goto, continue,即在顺序结构学习var,const,在分支结构中学习if, else, switch, case, default, fallthrough,在循环结构中学习for, break, goto, continue。另外你最好注册一个Leetcode账号.

Leetcode习题9

先让我们来看下一个来自leetcode的例子,这个是一个比较好的例子,里面包含了循环,分支,顺序并且包含了上一章中

9. 回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

首先看到的是它的取值范围是int32的范围,这个很重要,事实上我们还没有学过数组,使得在解题遇到困难,当前仅有的办法只能使用整形来完成这个题目,你可以看到如果x2147483647,那么它的回文数是7463847412,是的,在这种情况下2147483647的反转数超过int32的范围,但是我需要用int64吗?先试试看。

注意原题中已经提供了这个函数结构

func isPalindrome(x int) bool {
}

为了方便我们编写代码,我们需要知道本函数的输入是x,输出是代表是否是回文数(是:true,否:false),return能返回输出。

解题方法就是负数必然不是回文数,对于非负整数,我们循环x64除10,通过x64%10获得最低位,并且把最低位当作最高位加入到px64中,这样我们得到了x的反转过来的数px64`,判断他们是否相等就可以了。

代码如下,提交通过了,我们花费了28ms,仅仅击败12%的人

func isPalindrome(x int) bool {
    if (x < 0) {
        return false
    }
    x64 := int64(x)
    px64 := int64(0)
    for x64 != 0 {
        px64 = px64*10 + x64%10
        x64 /= 10
    }
    return px64 == int64(x)
}

我们去掉int64的强制转化,居然也通过了只花费4ms,击败了97%的人,这不可思议。

func isPalindrome(x int) bool {
    if (x < 0) { //分支结构
        return false
    }
    x64 := x   //整形 顺序结构
    px64 := 0
    for x64 != 0 {  //循环结构
        px64 = px64*10 + x64%10 // 顺序结构
        x64 /= 10
    }
    return px64 == x //布尔型
}

那么int32也能通过吗? 将上述代码提交,不行,答案错误。

func isPalindrome(x int) bool {
    if (x < 0) {
        return false
    }

    x64 := int32(x)
    px64 := int32(x)
    for x64 != 0 {
        px64 = px64*10 + x64%10
        x64 /= 10
    }
    return px64 == int32(x)
}

那么利用这三次提交的结果,并结合2.go语言基础类型漫游的知识,我们还可以得出:

  1. 变量强制转化会耗时
  2. 编程中的变量的取值范围很重要
  3. Leetcode的判题系统是64位的

1.控制结构:顺序结构,分支结构,循环机构

2.基本数据类型:整形,布尔型

控制结构

声明和赋值

在顺序结构中声明和赋值是很重要的

var可以声明一个变量,而const则声明一个常量

package main

import "fmt"

func main() {
    var i1 int     //声明变量,默认值为0
    var i2 int = 1 //声明变量,初始化为1
    i3 := 2        //这是最常用的声明和初始化手段
    fmt.Println(i1, i2, i3)
    i4 := i2 + i3 //使用运算表达式赋值
    i3 *= i4      //使用运算表达式赋值
    fmt.Println(i3, i4)
    const ci1 int = 13 //声明常量,无法
    fmt.Println(ci1)
}

输出:

0 1 2
6 3
13

这里仅仅举例了整形int的声明

算术运算符

++

--

自增1

自减1

+=

-=

*=

/=

%=

自增

自减

自乘

自除

自模,取余

+

-

*

/

%

加法

减法

乘法

除法

模,取余

注意: 例如如3/2 在整型中是整除即3/2=1,在浮点型是3.0/2.0=1.5,模运算智能用于整数

位运算符

<<

>>

&

|

^

左移

右移

亦或

<<=

>>=

&=

|=

^=

自左移

自右移

自与

自或

自亦或

位运算符几乎我们这篇实用的编程用不到,但是这个概念也很重要,计算机底层事实上是这样工作的。

逻辑运算

&&

||

==

!=

相等

不等于

>=

<=

>

<

大于等于

小于等于

大于

小于

这些会在分支语句中大放异彩。

if 语句

if 语句有if,if-else以及if-else if-else结构,如下所示:

package main

import "fmt"

func main() {
    var input int
    fmt.Printf("请输入分数:")
    fmt.Scanf("%d", &input)
    if input < 60 { //if
        fmt.Println("1.不合格")
    }

    if input < 60 { //if-else
        fmt.Println("2.不合格")
    } else{
        fmt.Println("2.合格")
    }

    if input < 60 {//if-else if-else
        fmt.Println("3.不合格")
    } else if input < 70 {
        fmt.Println("3.合格")
    } else if input < 85 {
        fmt.Println("3.良好")
    } else  {
        fmt.Println("3.优秀")
    }
}

结果如下:

请输入分数:59
1.不合格
2.不合格
3.不合格

switch 语句

事实上switch 语句比if语句更为强大,在有多个分支时更为符合go语言的风格,完整代码如下:

package main

import "fmt"

func main() {
    var i int
    fmt.Printf("请输入分数:")
    fmt.Scanf("%d\n", &i)
    switch {
    case i < 60: //单个逻辑表达式
        fmt.Println("不合格")
    case i < 70:
        fmt.Println("合格")
    case i < 85:
        fmt.Println("良好")
    default:
        fmt.Println("优秀")
    }

    var c byte
    fmt.Printf("请输入等级:")
    fmt.Scanf("%c\n", &c)

    switch c {
    case 'E', 'e': //可以有多个选择
        fmt.Println("1.不合格")
    case 'D', 'd':
        fmt.Println("1.基本合格")
    case 'C', 'c':
        fmt.Println("1.合格")
    case 'B', 'b':
        fmt.Println("1.良好")
    case 'A', 'a':
        fmt.Println("1.优秀")
    default:
        fmt.Println("1.错误的输入")
    }

    switch {
    case c == 'E', c == 'e': //可以有多个表达式
        fmt.Println("2.不合格")
    case c == 'D', c == 'd':
        fmt.Println("2.基本合格")
    case c == 'C', c == 'c':
        fmt.Println("2.合格")
    case c == 'B', c == 'b':
        fmt.Println("2.良好")
    case c == 'A', c == 'a':
        fmt.Println("2.优秀")
    default:
        fmt.Println("2.错误的输入")
    }

    switch {
    case c == 'E':
        fmt.Println("3.不合格")
        fallthrough //fallthrough会执行下一个case区块
    case c == 'e':
        fmt.Println("3.真的不合格")
    case c == 'D', c == 'd':
        fmt.Println("3.基本合格")
    case c == 'C', c == 'c':
        fmt.Println("3.合格")
    case c == 'B', c == 'b':
        fmt.Println("3.良好")
    case c == 'A', c == 'a':
        fmt.Println("3.优秀")
    default:
        fmt.Println("3.错误的输入")
    }

    var in interface{} = i
    switch data := in.(type) { //类型推断
    case int:
        fmt.Printf("int: %v\n", data)
    case uint:
        fmt.Printf("uint: %v\n", data)
    default:
        fmt.Printf("type: %T\n", data)
    }
}

结果如下:

请输入分数:90
优秀
请输入等级:E
1.不合格
2.不合格
3.不合格
3.真的不合格
int: 90

逻辑表达式

注意case可以是单个或多个逻辑表达式

    switch {
    case c == 'E', c == 'e': //可以有多个表达式
        fmt.Println("2.不合格")。
    case c == 'D', c == 'd':
        fmt.Println("2.基本合格")
    case c == 'C', c == 'c':
        fmt.Println("2.合格")
    case c == 'B', c == 'b':
        fmt.Println("2.良好")
    case c == 'A', c == 'a':
        fmt.Println("2.优秀")
    default:
        fmt.Println("2.错误的输入")
    }

fallthrough

fallthrough会执行下一个case区块

    switch {
    case c == 'E':
        fmt.Println("3.不合格")
        fallthrough //fallthrough会执行下一个case区块
    case c == 'e':
        fmt.Println("3.真的不合格")
    case c == 'D', c == 'd':
        fmt.Println("3.基本合格")
    case c == 'C', c == 'c':
        fmt.Println("3.合格")
    case c == 'B', c == 'b':
        fmt.Println("3.良好")
    case c == 'A', c == 'a':
        fmt.Println("3.优秀")
    default:
        fmt.Println("3.错误的输入")
    }

类型推断

这个是一个强大的方式,它可以用于推断go语言的接口的类型,不过现在只能简单那介绍一下,你可以将interface{}可以表达任何类型

    var in interface{} = i
    switch data := in.(type) { //类型推断
    case int:
        fmt.Printf("int: %v\n", data)
    case uint:
        fmt.Printf("uint: %v\n", data)
    default:
        fmt.Printf("type: %T\n", data)
    }

循环语句只有for

package main

import "fmt"

func main() {
    var input int
    fmt.Printf("请输入分数(0-5):")
    fmt.Scanf("%d", &input)

    for i := 0; i < 5; i++ { //正常的for
        fmt.Println("loop1:", i)

        if i < 2 {
            continue //跳过本次执行
        }
        if i == input {
            fmt.Println("loop1: break") //跳出本层循环
            break
        }
    }
    i := 0
Loop:
    for i < 5 { //去掉;的for循环
        fmt.Println("loop2:", i)
        for j := 0; j < 5; j++ {
            if j == input {
                fmt.Println("loop2: break Loop") //跳出Loop标记的循环
                break Loop
            }
            if j == 1 {
                break //跳出本层循环
            }
        }
        i++
    }
    i = 0
    for ; i < 5; i++ { //空缺一个元素并带有;的for循环
        fmt.Println("loop3:", i)
        for j := 0; j < 5; j++ {
            if j < 2 {
                continue
            }
            if j == input {
                goto Exit //跳出到Exit
            }
        }
    }
    return
Exit:
    fmt.Println("loop3: Exit")
}

结果如下:

请输入分数(0-5):1
loop1: 0
loop1: 1
loop1: 2
loop1: 3
loop1: 4
loop2: 0
loop2: break Loop
loop3: 0
loop3: 1
loop3: 2
loop3: 3
loop3: 4

请输入分数(0-5):2
loop1: 0
loop1: 1
loop1: 2
loop1: break
loop2: 0
loop2: 1
loop2: 2
loop2: 3
loop2: 4
loop3: 0
loop3: Exit

continue

跳过本次循环

break

没有加标签的就是跳过本层循环

加标签的就是跳过被标签标记的循环

goto

跳到被标签标记的循环,使用goto在处理一些同意的错误或者统一的出口而降低代码的冗余,增加代码可读性。这里挖个坑,之后我会在go-etl展示goto的魅力

注意:很多书籍指出goto会破环代码结构,降低代码可读性,那是因为这些书籍讲述使用goto的场景错了

Leetcode习题69

我们以leetcode习题开始,而现在有以一道Leetcode习题结束

69. x 的平方根

给你一个非负整数 x ,计算并返回 x算术平方根

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5

这道题会比Leetcode习题1更加困难些,这次数据范围是int32的非负整数范围,由于不能使用指数函数和算符,为此,在这里我们需要使用二分法,即通过二分[a,b](mid = a + (b-a)/2, 第一轮a=0,b=x)去获取mid=x/2,如果mid*mid<x 那么此时二分[a,mid-1],反之二分[mid+1,b], 这样不断二分下去直到 mid*mid=x或者a<b。好的,这样我们获得了解题思路,但是还有个问题,选择y的数据类型是什么,如果选择int32,那么相乘必然超过int32, 为此我们必须选择int64,注意:在Leetcode习题9中得出了结论,int是64位的,所以不用改为int64

func mySqrt(x int) int {
    a, b := 0, x
    for a <= b {
        mid := a + (b-a)/2   //二分区间
        if mid*mid == x {
            return mid
        }
        if mid*mid < x {
            a = mid + 1  //选择上面的区间
        } else {
            b = mid - 1
        }
    }
    return b
}

本篇小结

恭喜你已经完成了所有控制结构的学习,你以及知道了所有控制结构用到的保留字和注意点。另外,通过Leetcode习题69和Leetcode习题9,你已经知道在编程时选择数据类型的重要性,并且练习了所有控制结构。这里的相关代码放在go语言学习的go-base/3中,请下载后进行实际的联系

注意:之后的Leetcode题目解答以及使用工具的编程中,数据类型的选择以及控制结构的应用是非常重要的,也是最为基础的

下篇预告

go语言复合类型