Golang分支循环随机数
分支、循环、随机数
单分支
if condition {
代码块
}
if 5>2 {
fmt.Println("5大于2")
}
注意:Go语言中,花括号一定要跟着if、for、func等行的最后,否则语法出错。这其实就是为了解决C风格、Java风格之争。
- condititon必须是一个bool类型,在GO中,不能使用其他类型等效为布尔值。
if 1{}
是错误的 - 语句块中可以写其他代码
- 如果conditition为true,才能执行其后代码块
多分支
if condition1 {
代码块1
} else if condition2 {
代码块2
} else if condition3 {
代码块3
} ... {
...
} else if conditionN {
代码块N
} else {
代码块
}
a := 6
if a < 0 {
fmt.Println("negative")
} else if a > 0 { // 走到这里一定 a 不小于 0
fmt.Println("positive")
} else { // 走到这里一定 a 不大于、也不小于 0
fmt.Println("zero") }
- 多分支结构,从上向下依次判断分支条件,只要一个分支条件成立,其后语句块将被执行,那么其他条件都不会被执行
- 前一个分支条件被测试过,下一个条件相当于隐含着这个条件
- 一定要考虑一下else分之是否有必要写,以防逻辑漏洞
// 嵌套
a := 6
if a == 0 {
fmt.Println("zero")
} else {
if a > 0 {
fmt.Println("negative")
} else if a >= 0 { // 走到这里一定 a 不小于 0
fmt.Println("positive")
}
}
循环也可以互相嵌套,形成多层循环, 循环嵌套不宜过深。
switch 分支
特别注意:GO语言的switch有别于C语言的switch,case是独立代码块,不能穿透。
a := 20
switch a { // 待比较的是a
case 10:
fmt.Println("ten")
case 20:
fmt.Println("twenty")
case 30, 40, 50: // 或关系
fmt.Println(">=30 and <=50")
default:
fmt.Println("other") }
或写成
switch a:=20;a { // 待比较的是a
case 10:
fmt.Println("ten")
case 20:
fmt.Println("twenty")
case 30, 40, 50: // 或关系
fmt.Println(">=30 and <=50")
default:
fmt.Println("other") }
// 上面2种写法a的作用域有区别,这个后面再说
a := 20
switch { // 没有待比较变量,意味着表达式是true,是布尔型
case a > 0:
fmt.Println("positive")
case a < 0:
fmt.Println("negative")
default:
fmt.Println("zero") }
或写成
switch a := 20; { // 没有待比较变量,意味着表达式是true,是布尔型
case a > 0: // 如果待比较值是true,a > 0如果返回true,就进入
fmt.Println("positive")
// fallthrough // 穿透
case a < 0: // 如果待比较值是true,a < 0如果返回true,就进入
fmt.Println("negative")
default:
fmt.Println("zero") }
// 上面2种写法a的作用域有区别,这个后面再说
C语言的switch有穿透效果,而Go语言没有。如果想在Go语言中实现穿透效果,使用fallthrough
穿透当前case
语句块。但是,大家使用C语言的时候,一般都不想要使用这种穿透效果,所以,如非必要,不要使用fallthrough
。
特殊if
switch可以写成 switch a:=20;a 这种形式,也就是可以在表达式a之前写一个语句后接一个分号。if也可以这样
if score, line := 99, 90; score > line {
fmt.Println("perfect")
} else {
fmt.Println("good")
} // score, line作用域只能是当前if语句
这种写法中定义idea变量作用域只能是当前if或switch
for循环
C风格循环
for [初始操作];[循环条件];[循环后操作] {
循环体
}
- 初始操作:第一次进入循环前执行,语句只能执行一次,之后不再执行
- 循环条件:要求返回布尔值,每次进入循环体前进行判断。如果每次条件满足返回true,就进入循环执行一次循环体;否则,循环结束
- 循环后操作:每次循环体执行完,在执行下一趟循环条件判断之前,执行该操作一次
for i := 0; i < 10; i++ {
fmt.Println(i)
} // 初始操作中的短格式定义的i的作用域只能在for中
// 特殊写法
for i := 5; i < 10; {}
for i := 5; ; {} // 没条件就相当于true
for i < 10 {} // for condition {},condition就是循环条件
for ;; {} // 死循环
// 死循环简写如下
for {} // 死循环 相对于 for true {}
continue
中止当前这一趟循环的执行,直接执行循环后操作,进入下一趟的条件判断
for i := 0; i < 10; i++ {
if i%2 == 0 {
continue
}
fmt.Println(i)
} // 请问执行结果是什么?1,3,5,7,9
break
终止当前循环的执行,直接结束
for i := 0; ; i++ {
if i%2 == 0 {
continue
}
fmt.Println(i)
if i >= 10 {
break
}
} // 请问执行结果是什么?1,3,5,7,9,11
除了break,函数的return结束函数执行,当然也能把函数中的循环打断。
goto和label
它会破坏结构化编程,但是确实能做到便利的无条件跳转
- 跳出多重循环使用,但是为什么要用多重循环??????
- 到同一处标签统一处理,例如统一错误处理,问题是,写个函数也能实现为啥用你???
goto需要配合标签label使用,label就像代码中的锚点,goto将无条件调到那里开始向后执行代码。
for i := 0; ; i++ {
if i%2 == 0 {
continue
}
fmt.Println(i)
if i > 10 {
goto condition
}
}
condition:
fmt.Println("done")
continue、break也可以指定label,方便某些循环使用。但是,建议不要这么写,弄不好就成了毛线团。
for range
类型 | 变量 | Range expression | 第一个值 | 第二个值 |
---|---|---|---|---|
array or slice | a | [n]E, *[n]E []E | index i int | a[i]E |
string | s | “abcd” | index i int utf-8字节偏移 | unicode值 rune |
map | m | map[K]V | key k K | m[k]V |
channel | c | chan E, <-chan E 通道或只读通道 |
element e E | 无 |
"测试" utf-8 编码为 "\xe6\xb5\x8b\xe8\xaf\x95"
for i, v := range "abcd测试" {
fmt.Printf("%d, %[2]d, %[2]c, %#[2]x\n", i, v) }
fmt.Println("\xe6\xb5\x8b\xe8\xaf\x95") // 6个字节
运行结果如下
0, 97, a, 0x61
1, 98, b, 0x62
2, 99, c, 0x63
3, 100, d, 0x64
4, 27979, 测, 0x6d4b
7, 35797, 试, 0x8bd5
测试
索引就是字节偏移量,从索引可以看出,中文在字符串中是utf-8编码,占3个字节。
但是for range读取字符串返回的是一个个字符(整数),而字符是ASCII或UNICODE对应的编码值。
%d 打印的是unicode值 %c 打印的是字符
arr := [5]int{1, 3, 5, 7, 9}
for i, v := range arr {
fmt.Println(i, v, arr[i])
}
// 只打印索引
for i := range arr {
fmt.Println(i, arr[i])
}
// 只打印值
for _, v := range arr {
fmt.Println(v) }
随机数
标准库”math/rand”
我们使用的是伪随机数,是内部写好的公式计算出来的。这个公式运行提供一个种子,有这个种子作为起始值开始计算。
- src := rand.NewSource(100),使用种子100 创建一个随机数源
- rand.New(rand.NewSource(time.Now().UnixNano())) ,利用当前时间的纳秒值做种子
- r10 := rand.New(src),使用源创建随机数生成器
- r10.Intn(5),返回[0,5)的随机整数
package main
import (
"fmt"
"math/rand"
)
func main() {
src := rand.NewSource(10)
r10 := rand.New(src)
r1 := rand.New(rand.NewSource(1))
for i := 0; i < 10; i++ {
fmt.Printf("%d, %d, %d\n", rand.Intn(5), r1.Intn(5), r10.Intn(5))
}
}
全局随机数生成器globalRand
- 他的种子默认为1,
var globalRand = New(&lockedSource{src: NewSource(1).(*rngSource)})
- 如果要改变globalRand的种子,就需要使用rand.Seed(2)修改种子
rand.Intn(5)
就是使用它生成随机数
从Go v1.20开始,globalRand已经改变为使用随机种子。可以查看官网https://go.dev/doc/go1.20
如果想使用旧版globalRand的行为,可以手动设定种子源rand.Seed(1)
或使用环境变量os.Setenv("GODEBUG","randautoseed=0")