Go 1.19.4 分支、循环、随机数-Day 03

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的优点:

  1. 简洁性:​​for range​​ 提供了一种简洁、易读的方式来遍历集合中的元素,而无需手动管理索引或迭代器。它自动处理了迭代过程中的细节,使得代码更加简洁和清晰。
  2. 类型安全:​​for range​​ 在遍历不同类型的集合时,会根据集合的类型自动推断循环变量的类型,从而保证了类型安全。比如,当遍历一个整数切片时,循环变量会被自动推断为 ​​int​​ 类型。
  3. 灵活性:​​for range​​ 适用于多种类型的集合,包括数组、切片、字符串、映射和通道。这使得它成为一种非常通用的循环结构,可以方便地用于处理不同类型的数据。
  4. 性能优化:​​for range​​ 在遍历切片和映射时,内部使用了优化后的迭代算法,以提高性能。它避免了不必要的内存分配和复制,使得循环操作更加高效。
  5. 并发安全:当用于遍历通道时,​​for range​​ 可以安全地在多个 goroutine 之间进行通信。它会阻塞直到通道中有值可读,或者通道被关闭。这使得它成为处理并发任务时的一个强大工具。
  6. 范围限制:​​for range​​ 只遍历集合中的元素,而不会暴露底层的索引或迭代器。这有助于限制对集合的修改,避免在迭代过程中引入错误或不可预期的行为。

2.6.2 何时使用for range

  1. 遍历数组、切片、字符串、映射(map)和通道(channel)等可迭代的数据结构时。 ​​for range​​提供了一种简洁、易读的方式来遍历这些集合中的元素,而无需手动管理索引或迭代器。它会自动处理迭代过程中的细节,使得代码更加简洁和清晰。
  2. 需要同时获取集合中元素的索引和值时。 通过​​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函数的解释

  1. main,是整个程序的入口,操作系统调用时,会先找main,从main函数内部开始执行。
  2. 在同一个project中,main函数只能定义一个,且必须在main包中。
  3. 在同一个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
		}
	}
}
  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值