1. 分支
1.1 if分支
1.1.1 单分支
package main
import "fmt"
func main() {
//if condition {
// 代码块
//}
if 5 > 2 {
fmt.Println("5 大于 2")
}
}
注意事项:
- condition这个地方,一定要是一个布尔类型,在Go中,不能使用其他类型等效为布尔值,如 if 1 {} 是错误的。
- 语句块中可以写其他代码。
- 如果condition为true,才能执行其后代码块。
1.1.2 多分支
使用多分支注意事项:
- 多分支结构,从上向下依次判断分支条件,只要一个分支条件成立,其后语句块将被执行,那么其他条件都不会被执行。
- 前一个分支条件被测试过,下一个条件相当于隐含着前一个分支的相反结果。
- 一定要考虑一下else分支是有必要写,以防逻辑漏洞。
1.1.2.1 传统写法
更推荐该方式,比较易读。
package main
import "fmt"
func main() {
a := 100
if a > 0 {
fmt.Println("positive")
} else if a < 0 { // 当上面的if执行后,意味着后面的if结果一定是和上面的if结果相反的(隐含相反条件)
fmt.Println("negative")
} else { // 注意:使用多分支时,要考虑else是否要写或不写,有可能会造成逻辑漏洞。
fmt.Println("zero")
}
}
=========== 调试结果 ===========
positive
package main
import "fmt"
func main() {
if name, score := "Tom", 88; score > 90 { //该方式,变量只能在if内部被调用
fmt.Println(name, "Perfect")
} else {
fmt.Println(name, "Good")
}
}
=========== 调试结果 ===========
Tom Good
1.1.2.2 嵌套写法
使用嵌套方式时,建议不要超过3层。
package main
import "fmt"
func main() {
a := 100
if a == 0 {
fmt.Println("zero")
} else {
if a > 0 {
fmt.Println("positive")
} else {
fmt.Println("negative")
}
}
}
=========== 调试结果 ===========
positive
1.1.3 小练习:判断一个数是正数、负数还是零
package main
import "fmt"
// 编写一个程序,判断一个数是正数、负数还是零。
func a(nums int) {
if nums == 0 {
fmt.Println("零")
} else if nums < 0 {
fmt.Println("负数")
} else if nums > 0 {
fmt.Println("正数")
} else {
panic("不是数字!")
}
}
func main() {
a(-1)
}
1.2 switch分支
特别注意: Go语言的switch有别于C语言的switch,case是独立代码块,不能穿透。
1.2.1 基本用法
package main
import "fmt"
func main() {
a := 100
switch a {
case 0: // if a == 0
fmt.Println(0)
case 100: // if a == 100
fmt.Println(100)
case -100, -10, -20: // 如果a=-100或者-10或者-20,都能匹配上
fmt.Println(-100)
default: // 相当于else
fmt.Println("other")
}
}
=========== 调试结果 ===========
100
1.2.2 使用条件表达式作为条件
package main
import "fmt"
func main() {
a := 0
switch { //如果switch和{}之间没有跟任何标识符,默认就是bool类型的true
case a < 100: // 所以这里就相当于case bool,并且只能用这种方式
fmt.Println(">100")
default:
fmt.Println("0")
}
}
=========== 调试结果 ===========
>100
package main
import "fmt"
func main() {
switch a := 10; { // 这样也表示默认值为bool
case a > 0:
fmt.Println("negative")
}
}
1.2.2.1 错误的方式
func main() {
a := 0
switch a {
case a > 100: // 错误的写法
fmt.Println(">100")
}
}
1.2.3 将变量定义到switch中(该方式用的较多)
package main
import "fmt"
func main() {
switch a := 10; a { // 该方式,变量只能在switch内部被调用
case 0:
fmt.Println(0)
case 100:
fmt.Println(100)
default:
fmt.Println("other")
}
}
=========== 调试结果 ===========
other
1.2.4 使用多个值进行比较
switch expression {
case value1, value2, value3:
// 执行语句块
default:
// 执行默认语句块
}
1.2.5 小练习:根据用户输入的月份数字(1-12),输出该月份的名称。
package main
import "fmt"
// 编写一个程序,根据用户输入的月份数字(1-12),输出该月份的名称。
func b(nums int) {
for i := 1; i < 13; i++ {
switch nums {
case i:
fmt.Print(i, "月")
default:
// panic("溢出")
goto inner
}
}
inner:
fmt.Println("溢出")
}
func main() {
b(12)
}
2. for循环
注意:Go语言中只有for循环,没有while循环。
原因:Go中的for循环本身可以实现类似while循环的功能,如死循环。
使用嵌套for循环时,不要嵌套过多,不然会耗尽cpu,且执行效率低下,最多2层(最好不要嵌套)。
当需要多层for循环嵌套执行时,该考虑是不是要换一种方式。
2.1 语法
for [初始操作];[循环条件];[循环后操作] {
循环体
}
(1)初始操作
第一次进入循环前执行,语句只能执行一次,之后不再执行。
(2)循环条件
要求布尔值,每次进入循环体前进行判断。如果每次条件满足,就进入循环执行一次循环体;否则,循环结束。
(3)循环后操作
每次循环体执行完,在执行下一趟循环条件判断之前,执行该操作一次。
2.2 示例
2.2.1 有限次数循环
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// i := 0: 初始执行操作,只会在for循环执行前执行一次,后期不会再执行。
// i < 5: 判断条件,每次能否进入循环体中执行代码,要看结果是否为true。
// i++: 当for循环执行完一次,i++就执行一次
}
=========== 调试结果 ===========
0
1
2
3
4
// 这样也可以,变量可以被其他地方调用
package main
import "fmt"
func main() {
i := 20
for i < 10 { // for循环会自动识别循环条件
fmt.Println(i)
}
}
func main() {
for i := 0; i < 5; {
fmt.Println(i)
i++ \\ 或者 i += 1
}
}
=========== 调试结果 ===========
0
1
2
3
4
// 或者这样也可以
package main
import "fmt"
func main() {
for i := 0; i < 5; i = i + 2 {
fmt.Println(i)
// i += 2,这样也行
}
}
=========== 调试结果 ===========
0
2
4
2.2.2 死循环
// 方式一
package main
import "fmt"
func main() {
for i := 0; i < 5; {
fmt.Println(i)
//i += 2 // 这就成了死循环了。相当于给定循环后操作为true
}
}
// 方式二
package main
import "fmt"
func main() {
for i := 0; ;{
fmt.Println(i)
}
}
// 方式三
package main
import "fmt"
func main() {
for ; ;{
fmt.Println(i)
}
}
// 方式四
package main
import "fmt"
func main() {
for {
}
}
2.3 continue
中止当前这一趟循环体的执行,直接执行“循环后操作”后,进入下一趟循环的条件判断。
注意: continue只作用在for中,就算它被其他语句包着,也是从内往外找,跳出离它最近的一个for循环,然后开始下一次循环条件判断。
如果是嵌套for循环,则无法影响第二个for。
2.3.1 只输出10以内的偶数(奇数跳过)
// 方式一
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
if i&1 == 0 {
fmt.Println(i)
}
}
}
=========== 调试结果 ===========
0
2
4
6
8
// 方式二
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
if i&1 == 1 {
continue
}
fmt.Println(i)
}
}
=========== 调试结果 ===========
0
2
4
6
8
2.4 break
停止当前for循环的执行,结束了,整个for循环不再执行,但不影响for循环体后面的代码执行。
break也是作用在它找到的第一个for循环上,如果是嵌套for循环,则无法影响第二个for。
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
if i&1 == 0 {
continue
}
fmt.Println(i)
if i > 6 {
break
}
}
fmt.Printf("xxx")
}
=========== 调试结果 ===========
1
3
5
7
xxx
2.5 goto和label
这是一个被很多语言尘封或者废弃的关键字,它会破坏结构化编程,但是确实能做到便利的无条件跳转。
- 跳出多重循环使用,但是问题是为什么要用多重循环?
- 到同一处标签处统一处理,例如统一错误处理。问题是,写个函数也可以实现。
有时候也能简化一些代码,但是它是双刃剑,不要轻易使用。
goto需要配合标签label使用,label就像代码中的锚点,goto将无条件跳到那里开始向后执行代码。
continue、break也可以指定label,方便某些循环使用。但是,建议不要这么写,弄不好就成了毛线团。
2.5.1 死循环
package main
import "fmt"
func main() {
for {
inner: // 自定义的label
for i := 0; i < 10; i++ {
if i&1 == 0 {
// continue
// 跳转到上面的label开始执行,死循环,永远都在if i&1 == 0
goto inner
}
fmt.Println(i)
if i > 6 {
break
}
}
fmt.Printf("xxx")
}
}
2.5.2 跳出循环
package main
import "fmt"
func main() {
for {
for i := 0; i < 10; i++ {
if i&1 == 0 {
continue
}
fmt.Println(i)
if i > 6 {
// break
goto inner
}
}
}
inner:
fmt.Println("跳转到指定标签,循环结束。")
}
=========== 调试结果 ===========
1
3
5
7
跳转到指定标签,循环结束。
2.6 for range
for range是Go语言特有的一种迭代结构,可以用于遍历数组、切片、字符串、map以及通道(channel)等可迭代的数据结构。
2.6.1 为什么要使用for range
for range的优点:
- 简洁性:for range 提供了一种简洁、易读的方式来遍历集合中的元素,而无需手动管理索引或迭代器。它自动处理了迭代过程中的细节,使得代码更加简洁和清晰。
- 类型安全:for range 在遍历不同类型的集合时,会根据集合的类型自动推断循环变量的类型,从而保证了类型安全。比如,当遍历一个整数切片时,循环变量会被自动推断为 int 类型。
- 灵活性:for range 适用于多种类型的集合,包括数组、切片、字符串、映射和通道。这使得它成为一种非常通用的循环结构,可以方便地用于处理不同类型的数据。
- 性能优化:for range 在遍历切片和映射时,内部使用了优化后的迭代算法,以提高性能。它避免了不必要的内存分配和复制,使得循环操作更加高效。
- 并发安全:当用于遍历通道时,for range 可以安全地在多个 goroutine 之间进行通信。它会阻塞直到通道中有值可读,或者通道被关闭。这使得它成为处理并发任务时的一个强大工具。
- 范围限制:for range 只遍历集合中的元素,而不会暴露底层的索引或迭代器。这有助于限制对集合的修改,避免在迭代过程中引入错误或不可预期的行为。
2.6.2 何时使用for range
- 遍历数组、切片、字符串、映射(map)和通道(channel)等可迭代的数据结构时。 for range提供了一种简洁、易读的方式来遍历这些集合中的元素,而无需手动管理索引或迭代器。它会自动处理迭代过程中的细节,使得代码更加简洁和清晰。
- 需要同时获取集合中元素的索引和值时。 通过for range,我们可以同时获取到集合中每个元素的索引和值,这在某些场景下非常有用,比如当我们需要根据索引来修改集合中的元素时。
2.6.3 for range使用示例
2.6.3.1 遍历字符串
package main
import "fmt"
func main() {
// "abcde测"通过range来迭代的时候,会从左到右自动生成索引,从0开始。
// i表示索引,v表示value
for i, v := range "abcde测" { // 这里的for range默认按照unicode值取的
fmt.Printf("%d: %T %[2]v\n", i, v)
}
}
=========== 调试结果 ===========
0: int32 97 // a的索引为0,数据类型为int32说明默认是rune类型,对应ASCII码为97
1: int32 98
2: int32 99
3: int32 100
4: int32 101
5: int32 27979 // 汉字的话,这里实际取的就是uniocode值
2.6.3.2 使用索引取字符串的长度
用len操作字符串时,返回的是对应字符的字节数
package main
import "fmt"
func main() {
s := "abcde测"
for i := 0; i < len(s); i++ {
fmt.Printf("%d: %T %[2]v\n", i, s[i])
}
}
=========== 调试结果 ===========
0: uint8 97
1: uint8 98
2: uint8 99
3: uint8 100
4: uint8 101
5: uint8 230 // 5-7,是测这个字,对应的utf-8编码,3字节
6: uint8 181 // 取长度和取索引是不一样的,返回的是字节数
7: uint8 139
2.6.3.3 遍历数组
// 方式一
package main
import "fmt"
func main() {
arr := [5]int{1, 3, 6, 9, 19}
for i, v := range arr {
fmt.Println(i, v)
}
}
=========== 调试结果 ===========
0 1
1 3
2 6
3 9
4 19
// 方式二
package main
import "fmt"
func main() {
arr := [5]int{1, 3, 6, 9, 19}
// for i, v := range arr {
// fmt.Println(i, v)
// }
for i := 0; i < len(arr); i++ {
fmt.Println(i, arr[i]) // 没有明确的取值的时候,可以使用索引取值
}
}
// 方式三:只打印索引
package main
import "fmt"
func main() {
arr := [5]int{1, 3, 6, 9, 19}
for i := range arr {
fmt.Println(i)
}
}
=========== 调试结果 ===========
0
1
2
3
4
// 方式四:只打印值
package main
import "fmt"
func main() {
arr := [5]int{1, 3, 6, 9, 19}
for _, v := range arr { // 必须要加_,占位,不然一个v的话,会人为是显示索引
fmt.Println(v)
}
}
=========== 调试结果 ===========
1
3
6
9
19
2.7 小练习
2.7.1 编写一个程序,遍历一个字符串的每个字符,并打印出来。
package main
import "fmt"
// 编写一个程序,遍历一个字符串的每个字符,并打印出来。
func c(str string) {
for _, v := range str {
println(string(v))
}
}
func main() {
c("abc溜溜溜")
}
2.7.2 打印出 1 到 20 中的所有奇数,但是遇到 13 时跳过不打印,并在遇到 17 时停止循环。
package main
import "fmt"
// 编写一个程序,打印出 1 到 20 中的所有奇数,但是遇到 13 时跳过不打印,并在遇到 17 时停止循环。
func d() {
for i := 1; i < 21; i++ {
if i&1 == 1 {
if i == 13 {
fmt.Println("十三,直接跳过")
continue
} else if i == 17 {
fmt.Println("17,直接结束")
break
} else {
fmt.Println(i)
}
}
}
}
func main() {
d()
}
2.7.3 使用 goto 和 label 来实现一个简单的循环,当用户输入 ‘q’ 时退出循环。
package main
import "fmt"
// 使用 goto 和 label 来实现一个简单的循环,当用户输入 'q' 时退出循环。
func e(nums string) {
for {
if nums == "q" {
goto inner
}
fmt.Println("这是一个死循环!!!")
}
inner:
fmt.Println("解除死循环!!")
}
func main() {
e("q")
}
3. 伪随机数
生成随机数需要使用标准库:“math/rand”。
"math/rand"生成的是伪随机数,是内部写好的公式计算出来的。这个公式运行要提供一个种子,由这个种子作为起始值开始计算,可以生成整型或者浮点型的伪随机数。
rand.New(rand.NewSource(time.Now().UnixNano())) ,利用当前时间的纳秒值做种子,这种方式就算知道公式,也算不出来,因为时间一直在变。
r10 := rand.New(src),使用源创建随机数生成器
r10.Intn(5),返回[0, 5)的随机整数,包前不包后,不含5
rand.Intn(10),不需要种子,rand包内部自带随机数生成器,种子固定为1,可修改。
3.1 方式一:rand.New(rand.NewSource(seed))
rand.New(rand.NewSource(seed))含义:
(1)rand.New()
这个函数用来创建一个新的随机数生成器(*rand.Rand),它使用指定的随机数源(source)来生成随机数。
(2)rand.NewSource()
这个函数用来创建一个新的随机数源(rand.Source)。
这个源是伪随机数生成器的核心,它基于一个初始的种子值(seed)来生成一系列的随机数。
(3)seed
随机数种子,相同的种子值会导致生成相同的随机数序列。因此,如果每次都用相同的种子值来创建随机数源,那么每次得到的随机数序列都会是一样的。
3.1.1 创建10个随机数
该方式可以通过公式计算出下一个随机数
package main
import (
"fmt"
"math/rand"
)
func main() {
// 100,相当于是随机数的种子,通过这个种子构造一个源,通过这个源构造一个随机数生成器
r := rand.New(rand.NewSource(100))
for i := 0; i < 10; i++ {
fmt.Println(i, r.Int()) // r.Int,返回一个非负的伪随机数
}
}
=========== 调试结果 ===========
0 7530908113823513298
1 8856843482472596024
2 557474217132159819
3 2877121541507365039
4 6731085038606570514
5 1816137624592625432
6 5282924661529872796
7 4077108211906745666
8 4808754755028350735
9 8704958202260308128
3.1.2 创建10个不超过10的随机数
该方式可以通过公式计算出下一个随机数
package main
import (
"fmt"
"math/rand"
)
func main() {
r := rand.New(rand.NewSource(100))
for i := 0; i < 10; i++ {
// r.Intn,Intn作为int返回半开区间[0,n](包前不包后)中的非负伪随机数。
// 如果n <= 0,它就会恐慌。
fmt.Println(i, r.Intn(10)) // r.Intn(10),表示0-9,不含10
}
}
=========== 调试结果 ===========
0 3
1 8
2 0
3 0
4 2
5 2
6 9
7 9
8 4
9 1
3.2 方式二:使用时间纳秒生成随机数
该方式比上面的好一点,因为就算知道公式,但是不知道准确的时间,也无法推算出对应的值。rand.New(rand.NewSource(time.Now().UnixNano()))含义:
time.Now().UnixNano():获取当前时间的 Unix 纳秒时间戳。
注意:这个纳秒时间戳是从 Unix 纪元(1970 年 1 月 1 日 00:00:00 UTC)开始的,而不是从 Go 程序的启动时间开始的。这意味着,无论你的 Go 程序何时启动,time.Now().UnixNano() 总是返回从 Unix 纪元到当前时间的纳秒数。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 10; i++ {
fmt.Println(i, r.Intn(10))
}
}
=========== 调试结果 ===========
0 1
1 9
2 8
3 8
4 2
5 5
6 5
7 9
8 1
9 7
3.3 方式三:直接使用rand.Intn生成随机数
全局随机数生成器globalRand。
- 它的种子默认为1
- 如果要改变globalRand的种子,就需要使用rand.Seed(2)修改种子
- rand.Intn(5)就是使用它生成随机数
3.3.1 常规使用
package main
import (
"fmt"
"math/rand"
)
func main() {
for i := 0; i < 5; i++ {
fmt.Println(i, rand.Intn(5))
}
}
=========== 调试结果 ===========
0 1
1 2
2 2
3 4
4 1
3.3.2 修改rand.Intn固定种子值
package main
import (
"fmt"
"math/rand"
)
func main() {
for i := 0; i < 5; i++ {
fmt.Println(i, rand.Intn(5))
}
fmt.Println("----------------------")
rand.Seed(2) // 修改rand.Intn固定的种子值
for i := 0; i < 5; i++ {
fmt.Println(i, rand.Intn(5))
}
}
=========== 调试结果 ===========
0 1
1 2
2 2
3 4
4 1
----------------------
0 1
1 1
2 2
3 0
4 4
3.3.3 使用时间纳秒作为种子
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
for i := 0; i < 5; i++ {
fmt.Println(i, rand.Intn(5))
}
fmt.Println("-----------------")
rand.Seed(time.Now().UnixNano())
for i := 0; i < 5; i++ {
fmt.Println(i, rand.Intn(5))
}
}
=========== 调试结果 ===========
0 1
1 2
2 2
3 4
4 1
-----------------
0 2
1 3
2 1
3 0
4 1
3.4 小练习:生成一个 1 到 10 之间的随机数,并询问用户猜测这个数,根据用户的猜测给出提示.
package main
import (
"fmt"
"math/rand"
"time"
)
// 生成一个 1 到 10 之间的随机数,并询问用户猜测这个数,根据用户的猜测给出提示.
func f() {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
var a int
b := r.Intn(10)
fmt.Println("请输入一个正整数:")
_, err := fmt.Scan(&a)
if err != nil {
fmt.Println("异常")
} else if a == b {
fmt.Printf("正确。输入:%v。输出:%v", a, b)
} else {
fmt.Printf("不正确。输入:%v。输出:%v", a, b)
}
}
func main() {
f()
}
4. 关于main函数的解释
- main,是整个程序的入口,操作系统调用时,会先找main,从main函数内部开始执行。
- 在同一个project中,main函数只能定义一个,且必须在main包中。
- 在同一个project中,多份代码文件中使用的包名,必须一致,如main.go中,定义了package main后,b.go中也只能这么定义。
5. 练习题
5.1 打印99乘法口诀表
5.1.1 任务拆解:先写框架,99乘法表,最多1-9,所以先打印出9行
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
fmt.Println(i)
}
}
================调试结果===============
1
2
3
4
5
6
7
8
9
5.1.2 任务拆解:在每一行里面,再输出9列
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
// fmt.Println(i)
for j := 1; j < 10; j++ {
fmt.Println(i, j)
}
}
}
================调试结果===============
// 到这里,会发现结果输出了9*9=81行,是因为:
// 首先i * j,i=1,进入到j=1,那么1*1,内部的for会循环9次,i会一直*到9。
// 然后内部for循环完毕,到外部,i=2,到内部就是2*1,一直循环下去,就是81行。
// 那怎么解决这个问题呢?如下:
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
// fmt.Println(i)
for j := 1; j < 10; j++ {
fmt.Print(i, j, " ") // 修改点一
}
fmt.Println() // 修改点二:内部循环到9结束,打印一个换行
}
}
================调试结果===============
1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9
2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9
3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9
4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9
5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9
6 1 6 2 6 3 6 4 6 5 6 6 6 7 6 8 6 9
7 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9
8 1 8 2 8 3 8 4 8 5 8 6 8 7 8 8 8 9
9 1 9 2 9 3 9 4 9 5 9 6 9 7 9 8 9 9
// 格式化输出上面的结果
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
// fmt.Println(i)
for j := 1; j < 10; j++ {
fmt.Printf("%d*%d=%d ", j, i, j*i) // 修改点
}
fmt.Println()
}
}
// 格式化输出上面的结果
1*1=1 2*1=2 3*1=3 4*1=4 5*1=5 6*1=6 7*1=7 8*1=8 9*1=9
1*2=2 2*2=4 3*2=6 4*2=8 5*2=10 6*2=12 7*2=14 8*2=16 9*2=18
1*3=3 2*3=6 3*3=9 4*3=12 5*3=15 6*3=18 7*3=21 8*3=24 9*3=27
1*4=4 2*4=8 3*4=12 4*4=16 5*4=20 6*4=24 7*4=28 8*4=32 9*4=36
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 6*5=30 7*5=35 8*5=40 9*5=45
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 7*6=42 8*6=48 9*6=54
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 8*7=56 9*7=63
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 9*8=72
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
// 上面已经有个大概的结果了,这个时候我们想要的是这样的
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
5.1.3 任务拆解:把最终结果输出为三角形
通过观察可以发现,i j相等的部分保留,后面的都不要,最终可以形成我们想要的形状。
如下红线部分删除。
如下代码:
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
// fmt.Println(i)
for j := 1; j < 10; j++ {
if j <= i { // 只要j不大于i,就打印
fmt.Printf("%d*%d=%d ", j, i, j*i)
}
//fmt.Printf("%d*%d=%d ", j, i, j*i)
// ij相等的部分打印完毕后,就直接退出这个for循环,开始下一个外部for循环
//if i == j {
// break
//}
}
fmt.Println()
}
}
================调试结果===============
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
// 简略写法
// 首先i最大到9,j也是最大也是到9,所以if可以省略不写,如下:
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
for j := 1; j <= i; j++ { // 就让j小于等于i即可
// if j <= i {
fmt.Printf("%d*%d=%d ", j, i, j*i)
// }
}
fmt.Println()
}
}
5.1.4 任务拆解:调整间隔均匀
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
for j := 1; j <= i; j++ {
if i == 1 {
fmt.Printf("%d*%d=%-2d", j, i, j*i) // 左对齐2个字符
} else {
fmt.Printf("%d*%d=%-3d", j, i, j*i) // 左对齐3个字符
}
}
fmt.Println()
}
}
================调试结果===============
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
5.1.5 整间隔均匀另一种方式
package main
import "fmt"
func main() {
var width int // 定义一个零值
for i := 1; i < 10; i++ {
for j := 1; j <= i; j++ {
if i == 1 {
width = 2
} else {
width = 3
}
// "%d*%d=%-[4]*[3]d",这部分含义
// -[4]:表示索引为4的width,也就是左对齐的宽度
// [3]d:表示d对应索引j*i,不指定的话会顺延使用5。
// 如果直接-[4]d的话,go会认为d的值就是4
// *的作用就是分割,让两边的标识符都有自己独自的含义
fmt.Printf("%d*%d=%-[4]*[3]d", j, i, j*i, width)
}
fmt.Println()
}
}
================调试结果===============
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
5.2 随机生成20个100以内的非0正整数,打印出来,并对生成的数值,第奇数个(不是索引)累加求和,第偶数个累乘求积,打印结果。
package main
import (
"fmt"
"math/rand"
)
func main() {
jia := 0
cheng := 1
r := rand.New(rand.NewSource(100)) // 创建随机数生成器
for i := 1; i <= 20; i++ { // 循环20次
// a := r.Intn(100)
a := r.Intn(99) + 1// 0-98,+1变成1-99
if a&1 == 1 { // 打印奇数。i % 2 也行
jia += a
fmt.Println("第", i, "个奇数:", a)
} else if a&1 == 0 { // 打印偶数
cheng *= a
fmt.Println("第", i, "个偶数:", a)
}
}
fmt.Println("奇数累加和为:", jia)
fmt.Println("偶数累乘积为:", uint64(cheng)) // 防止超界。防止显示有符号负数。
}
5.3 打印100以内的斐波那契数列
斐波那契数列的特点:0、1、1、2、3……
从第三个数字开始,是前两个数字相加之和。
5.3.1 方式一
5.3.1.1 任务拆解:先写框架
package main
import (
"fmt"
)
func main() {
// 先给出两个变量,必须要给,让循环从ab开始
a, b := 1, 1
fmt.Println(a, b)
}
================调试结果===============
1 1
5.3.1.2 任务拆解:循环打印斐波那契数列
// 计算公式:a = b, b += a
// 第一次循环:a = 1, b = 1 第一次不带入公式
// 第二次循环:a = b(1), b += a(2)
// 大概的意思就是:a, b = b, a+b。a+b表示的就是第三个数字
// 注意:在go中,如b += a(2),这里的a表示的a的旧值,不是a = b(1)后的值。
package main
import (
"fmt"
)
func main() {
// 先给出两个变量,必须要给,让循环从ab开始
a, b := 1, 1
fmt.Println(a, b)
for {
a, b = b, a+b
fmt.Println(a, b)
break
}
}
================调试结果===============
1 1
1 2 // 通过输出可以看到,代码中b就是前两项之和,后续把a去掉不打印就行。
5.3.1.3 任务拆解:循环打印100以内的斐波那契数列
package main
import (
"fmt"
)
func main() {
// 先给出两个变量,必须要给,让循环从ab开始
a, b := 1, 1
fmt.Println(a, b)
for {
a, b = b, a+b
if b > 100 { // 添加if判断控制,输出不会超过100
break
}
fmt.Println(b)
}
}
================调试结果===============
0
1
1
2
3
5
8
13
21
34
55
89
5.3.2 方式二:函数传参方式
package main
import (
"fmt"
)
func two(num int) {
a, b := 0, 1
fmt.Print(a, "\n", b, "\n")
for {
a, b = b, a+b
if b > num {
break
}
fmt.Println(b)
}
}
func main() {
two(100)
}
5.3.3 方式三
package main
import (
"fmt"
)
func main() {
num1 := 0
num2 := 1
fmt.Print(num1, "\n", num2, "\n")
for {
num1, num2 = num2, num1+num2 // Go语言的多重赋值是同时执行的
if num2 <= 100 {
fmt.Println(num2)
} else {
break
}
}
}