首页 > 其他分享 >【读书笔记&个人心得】第5章:控制结构

【读书笔记&个人心得】第5章:控制结构

时间:2023-03-01 12:00:10浏览次数:38  
标签:case 读书笔记 fmt Character 控制结构 func position main 心得

if-else

Go 的 if 不用写括号

if condition1 {
	// do something
} else if condition2 {
	// do something else
} else {
	// catch-all or default
}

if 与{要在同一行,} else { 这三者要同一行

不建议同时在 if-else 结构的两个分支里都使用 return 语句

if condition {
	return x
}
return y

判断一个字符串是否为空

if str == "" { ... }
if len(str) == 0 {...}

判断系统环境

var prompt = "Enter a digit, e.g. 3 "+ "or %s to quit."

func init() {
	if runtime.GOOS == "windows" {
		prompt = fmt.Sprintf(prompt, "Ctrl+Z, Enter")
	} else { //Unix-like
		prompt = fmt.Sprintf(prompt, "Ctrl+D")
	}
}

if 可以包含一个初始化语句

在 if 初始化的语句,只在该 if-else 中有效

一般写法

val := 10
if val > 12 {
	// do something
}

使用 if 包含初始化,先写初始化,然后加;号,再写 if 的条件,如果变量在 if 结构之前就已经存在,那么在 if 结构中,该变量原来的值会被隐藏(覆盖原理)

package main

import "fmt"

func main() {
	val := 1
	fmt.Println(val)// 1
	if val := 10; val > 5 {
		fmt.Println(val) //5
	}

}
package main

import "fmt"

func main() {
	if val := GetFood(); val == "apple" {
		fmt.Println("the food is " + val) //the food is apple
	}
}

func GetFood() string {
	return "apple"
}

测试多返回值函数的错误

其实就个
try{}catch(E e){}

一个函数会发生错误时,会返回两个值,一个是执行结果,一个是执行是否成功,对于后者,有两种类型:
1 返回是否成功:true 表示成功,0、false 和 nil 表示失败
2 返回是否出错:成功的话,error 为 nil,失败的话,error 包含相应的错误信息。error: var err error

习惯用法:

value, err := pack1.Function1(param1)
if err != nil {
	fmt.Printf("An error occured in pack1.Function1 with parameter %v", param1)
	return err
}
// 未发生错误,继续执行:

根据我的试验,return 时,在子函数时才 return,main 函数不 return,否则会提示 too many return values

发现错误终止程序的运行

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	value, err := strconv.Atoi("123")
	if err != nil {
		fmt.Printf("Program stopping with error %v", err)
		os.Exit(1)
	}
	fmt.Printf(strconv.Itoa(value))

}

获取结果并立即判断错误

// 返回err
if err := file.Chmod(0664); err != nil {
	fmt.Println(err)
	return err
}

// 返回是否成功 true、false
if value, ok := readData(); ok {
…
}


没有为多返回值的函数准备足够的变量来存放结果。编译将会报错,若想避免编译出错,可以用 _ 舍弃掉返回值,若能保证执行一定成功,那就加一层封装永久解决问题。

func atoi (s string) (n int) {
	n, _ = strconv.Atoi(s)
	return
}

实际上,fmt 包(第 4.4.3 节)最简单的打印函数也有 2 个返回值:

count, err := fmt.Println(x) // number of bytes printed, nil or 0, error

当打印到控制台时,可以将该函数返回的错误忽略;但当输出到文件流、网络流等具有不确定因素的输出对象时,应该始终检查是否有错误发生(另见练习 6.1b)。

switch

switch var1 {
	case val1:
		...
	case val2:
		...
	default:
		...
}

var1 可以时任意类型

支持单分支多匹配值

case val1, val2, val3

自动 break

一旦成功地匹配到某个分支,在执行完相应代码后就会退出整个 switch 代码块,还希望继续执行后续分支的代码,可以使用 fallthrough 关键字来达到目的

switch i {
	case 0: // 空分支,只有当 i == 0 时才会进入分支
	case 1:
		f() // 当 i == 0 时函数不会被调用
}

switch i {
	case 0: fallthrough
	case 1:
		f() // 当 i == 0 时函数也会被调用
}

package main

import (
	"fmt"
)

func main() {

	val := 3
	switch val {
	case 0:
	case 1, 2:
	case 3:
		fmt.Println("0没有匹配")
		fmt.Println("1没有匹配")
		return
		fmt.Printf("第三句不会被执行,因为使用了return提前结束整个函数的执行")
	}
}

输出:
0 没有匹配
1 没有匹配

注意:由于 switch 是分支结构,所以在代码块内的 return 不一定会执行,所以要在 switch 外也写 switch 来保证函数始终会返回(我觉得或许写在 default 中也可以)

default

当其他分支都无法匹配成功时,会匹配 default,default 可以放在任何位置,建议放在最后

链式的 if-else 的替代

switch {
	case i < 0:
		f1()
	case i == 0:
		f2()
	case i > 0:
		f3()
}

任何支持进行相等判断的类型都可以作为测试表达式的条件,包括 int、string、指针等

package main

import (
	"fmt"
)

func main() {

	i := 11
	var ptr *int = &i
	ptr2 := ptr
	fmt.Println(ptr, ptr2)
	switch {
	case i < 0:
		fmt.Println("执行f1()")
	case i == 0:
		fmt.Println("执行f2()")
	case i > 0:
		fmt.Println("执行f3()")
		fallthrough //继续往下执行
	case ptr == ptr2:
		fmt.Println("执行f4()")
	}

}

输出
0xc000018030 0xc000018030
执行 f3()
执行 f4()

switch 包含初始化

switch result := calculate(); {
	case result < 0:
		...
	case result > 0:
		...
	default:
		// 0
}

switch a, b := x[i], y[j]; {
	case a < b: t = -1
	case a == b: t = 0
	case a > b: t = 1
}

实现一个季节计算器

package main

import (
	"fmt"
	"strconv"
)

func main() {

	month := -1
	fmt.Scanf("%d", &month)
	switch month {
	case 12, 1, 2:
		fmt.Println("冬季")
	case 3, 4, 5:
		fmt.Println("春季")
	}

	switch {
	case month >= 6 && month <= 8:
		fmt.Println("夏季")
	case strconv.Itoa(month) == "9" || strconv.Itoa(month) == "10" || strconv.Itoa(month) == "11":
		fmt.Println("秋季")
	}

}

for

在 Go 中,循环用 for,for 很灵活,和 if 一样,for 省略了括号

先声明 i 并初始化,然后判断 i<5,然后执行 i++,判断 i<5,重复以上步骤

package main

import "fmt"

func main() {
	for i := 0; i < 5; i++ {
		fmt.Printf("This is the %d iteration\n", i)
	}
}

多个计数器

for i, j := 0, N; i < j; i, j = i+1, j-1 {}

使用按位取反取|1|到|10|

package main

import "fmt"

func main() {
	for i := 0; i <= 10; i++ {
		fmt.Printf("%2d|", ^i)
	}
	fmt.Printf("\n")
	for i := 0; i >= -10; i-- {
		fmt.Printf("%2d|", ^i)
	}
}

使用按位补码从 0 到 10

package main

import "fmt"

func main() {
	for i := 0; i <= 10; i++ {
		fmt.Printf("the complement of %b is: %b | %d is: %d\n", i, ^i, i, ^i)
	}
}

/* Output:
the complement of 0 is: -1
the complement of 1 is: -10
the complement of 10 is: -11
the complement of 11 is: -100
the complement of 100 is: -101
the complement of 101 is: -110
the complement of 110 is: -111
the complement of 111 is: -1000
the complement of 1000 is: -1001
the complement of 1001 is: -1010
the complement of 1010 is: -1011
*/

和 switch 连用

写一个从 1 打印到 100 的程序,但是每当遇到 3 的倍数时,不打印相应的数字,但打印一次 "Fizz"。遇到 5 的倍数时,打印 Buzz 而不是相应的数字。对于同时为 3 和 5 的倍数的数,打印 FizzBuzz(提示:使用 switch 语句)

package main

import "fmt"

const (
	FIZZ     = 3
	BUZZ     = 5
	FIZZBUZZ = 15
)

func main() {
	for i := 0; i <= 100; i++ {
		switch {
		case i%FIZZBUZZ == 0:
			fmt.Println("FizzBuzz")
		case i%FIZZ == 0:
			fmt.Println("Fizz")
		case i%BUZZ == 0:
			fmt.Println("Buzz")
		default:
			fmt.Println(i)
		}
	}
}

把 for 当成 while 用

就像 switch 后面不提供任何被判断的值(实际上默认为判断是否为 true),for 也可以没有初始化语句和修饰语句结构(但是有条件语句),因此 ;; 便是多余的了

package main

import "fmt"

func main() {
	var i int = 5

	for i >= 0 {
		i = i - 1
		fmt.Printf("The variable i is now: %d\n", i)
	}
}

无限循环(死循环)

条件语句是可以被省略的,如 i:=0; ; i++ 或 for { } 或 for ;; { }(;; 会在使用 gofmt 时被移除)
如果 for 循环的头部没有条件语句,那么就会认为条件永远为 true,变成 while 是只有条件语句

想要直接退出循环体,可以使用 break 语句(第 5.5 节)或 return 语句直接返回(函数的 return)(第 6.1 节)。这两者之间有所区别,break 只是退出当前的循环体,而 return 语句提前对函数进行返回,不会执行后续的代码。

无限循环的经典应用是服务器,用于不断等待和接受新的请求。

for t, err = p.Token(); err == nil; t, err = p.Token() {
	...
}

// 我的写法
for {
  t, err = p.Token()
  if err!=nil {
	  break;
  }
}

for-range 结构

这是 Go 特有的一种的迭代结构,您会发现它在许多情况下都非常有用。它可以迭代任何一个集合(包括数组和 map,详见第 7 和 8 章)。语法上很类似其它语言中的 foreach 语句,但您依旧可以获得每次迭代所对应的索引(很智能,可以智能化提供下一个索引,而不用借助迭代器)

for ix, val := range coll { }

val 始终为集合中对应索引的值拷贝,因此它一般只具有只读性质,对它所做的任何修改都不会影响到集合中原有的值(译者注:如果 val 为指针,则会产生指针的拷贝,依旧可以修改集合中的原值)

一个字符串是 Unicode 编码的字符(或称之为 rune)集合,因此您也可以用它迭代字符串:

for pos, char := range str {
...
}

每个 rune 字符和索引在 for-range 循环中是一一对应的。它能够自动根据 UTF-8 规则识别 Unicode 编码的字符。

package main

import "fmt"

func main() {
	str := "Go is a beautiful language!"
	fmt.Printf("The length of str is: %d\n", len(str))
	for pos, char := range str {
		fmt.Printf("Character on position %d is: %c \n", pos, char)
	}
	fmt.Println()
	str2 := "Chinese: 日本語"
	fmt.Printf("The length of str2 is: %d\n", len(str2))
	for pos, char := range str2 {
    	fmt.Printf("character %c starts at byte position %d\n", char, pos)
	}
	fmt.Println()
	fmt.Println("index int(rune) rune    char bytes")
	for index, rune := range str2 {
    	fmt.Printf("%-2d      %d      %U '%c' % X\n", index, rune, rune, rune, []byte(string(rune)))
	}
}

输出
The length of str is: 27
Character on position 0 is: G
Character on position 1 is: o
Character on position 2 is:
Character on position 3 is: i
Character on position 4 is: s
Character on position 5 is:
Character on position 6 is: a
Character on position 7 is:
Character on position 8 is: b
Character on position 9 is: e
Character on position 10 is: a
Character on position 11 is: u
Character on position 12 is: t
Character on position 13 is: i
Character on position 14 is: f
Character on position 15 is: u
Character on position 16 is: l
Character on position 17 is:
Character on position 18 is: l
Character on position 19 is: a
Character on position 20 is: n
Character on position 21 is: g
Character on position 22 is: u
Character on position 23 is: a
Character on position 24 is: g
Character on position 25 is: e
Character on position 26 is: !

The length of str2 is: 18
character C starts at byte position 0
character h starts at byte position 1
character i starts at byte position 2
character n starts at byte position 3
character e starts at byte position 4
character s starts at byte position 5
character e starts at byte position 6
character : starts at byte position 7
character   starts at byte position 8
character 日 starts at byte position 9
character 本 starts at byte position 12
character 語 starts at byte position 15

index int(rune) rune    char bytes
0       67      U+0043 'C' 43
1       104      U+0068 'h' 68
2       105      U+0069 'i' 69
3       110      U+006E 'n' 6E
4       101      U+0065 'e' 65
5       115      U+0073 's' 73
6       101      U+0065 'e' 65
7       58      U+003A ':' 3A
8       32      U+0020 ' ' 20
9       26085      U+65E5 '日' E6 97 A5
12      26412      U+672C '本' E6 9C AC
15      35486      U+8A9E '語' E8 AA 9E

U+65E5 的意思是这是一个 Unicode 字符,可以这样验证

package main

import (
	"fmt"
)

func main() {

	fmt.Printf("%s", "\u8A9E") // output 語
}

break 和 continue

break 只会退出最内层的循环

package main

func main() {
	for i:=0; i<3; i++ {
		for j:=0; j<10; j++ {
			if j>5 {
			    break
			}
			print(j)
		}
		print("  ")
	}
}

continue

continue 忽略剩余的循环体(剩余未执行的代码)而直接进入下一次循环的过程,但不是无条件执行下一次循环,执行之前依旧需要满足循环的判断条件

标签与 goto

使用全大写来对标签命名,如 LABEL1

例 1

package main

import "fmt"

func main() {

LABEL1:
	for i := 0; i <= 5; i++ {
		for j := 0; j <= 5; j++ {
			if j == 4 {
				continue LABEL1
			}
			fmt.Printf("i is: %d, and j is: %d\n", i, j)
		}
	}

}

输出:
j:0,i:0||
j:1,i:0||
j:2,i:0||
j:3,i:0||
j:0,i:1||
j:1,i:1||
j:2,i:1||
j:3,i:1||
j:0,i:2||
j:1,i:2||
j:2,i:2||
j:3,i:2||
j:0,i:3||
j:1,i:3||
j:2,i:3||
j:3,i:3||
j:0,i:4||
j:1,i:4||
j:2,i:4||
j:3,i:4||
j:0,i:5||
j:1,i:5||
j:2,i:5||
j:3,i:5||

例 2

package main

import "fmt"

func main() {
LABEL1:
	for i := 0; i <= 5; i++ {
		for j := 0; j <= 5; j++ {
			if j == 4 {
				continue LABEL1
			}
			fmt.Printf("j:%d,i:%d||", j, i)
			fmt.Printf("\n")
		}
		fmt.Printf("这一句永远不会被执行\n")
	}
}

例 1 中,continue 语句指向 LABEL1,当执行到该语句的时候,就会跳转到 LABEL1 标签的位置。
您可以看到当 j4 和 j5 的时候,没有任何输出:标签的作用对象为外部循环,因此 i 会直接变成下一个循环的值,而此时 j 的值就被重设为 0,即它的初始值

例 2 的第三个 Printf 因为内层循环每次变成 4 就会导致外层循环进入下一次,就像起了一个 continue 作业一样,第三个第三个 Printf 就被短路了

package main

import "fmt"

func main() {
LABEL1:
	for i := 0; i <= 5; i++ {
		for j := 0; j <= 5; j++ {
			if j == 4 {
				break LABEL1
			}
			fmt.Printf("j:%d,i:%d||", j, i)
			fmt.Printf("\n")
		}
		fmt.Printf("这一句永远不会被执行\n")
	}
}

输出:
j:0,i:0||
j:1,i:0||
j:2,i:0||
j:3,i:0||

由于标签的作用对象是外层循环,所以 break 也是 break 外层

不要使用 goto

标签:case,读书笔记,fmt,Character,控制结构,func,position,main,心得
From: https://www.cnblogs.com/ww01/p/17167693.html

相关文章

  • Python elasticsearch 使用心得
    一、配置python==3.6/3.8#更高版本的elasticsearch会出现doc_type被统一成_doc导致旧版语句报错的情况pipinstallelasticsearch==7.8.0二、连接esfromelastics......
  • 听说大家很感兴趣玮子的学习心得,采访来了
    哪有那么多人生开挂,不过都是厚积薄发——哲理熊上次玮子投稿以后,大家都很好奇,为什么他可以坚持在朋友圈打卡几百天,想技术问题那么有深度,今天就随熊哥走进科学,揭秘玮......
  • 听说大家很感兴趣玮子的学习心得,采访来了
    哪有那么多人生开挂,不过都是厚积薄发——哲理熊上次玮子投稿以后,大家都很好奇,为什么他可以坚持在朋友圈打卡几百天,想技术问题那么有深度,今天就随熊哥走进科学,揭秘玮......
  • 记录写了6年代码的心得
    心得:写代码的人有多舒服,运行代码的电脑就有多难受!强类型语言与弱类型语言的区别就能很好的体现现在的js的更新我个人没法理解,完全是奔着更多人去的如果未来的某天g......
  • 软件工程心得体会
    在以前,我一直对软件存在一些偏见或则是误解,认为软件就是程序,软件的开发就是编写程序,只要编完了程序,一切也就 ok 了,而且我还片面的认为只要我掌握了时下最新的语言和工具,......
  • 软件工程心得体会
    不知不觉已经学了计算机快两年了,在大一的时候接触了c和c++,大二上接触了Java和html的编写。现在对编程也有了一定的了解和认识,也清楚知道要熟练掌握一门语言的必要性,也了解......
  • Android 中的AsyncTask的使用心得
    Android中的AsyncTask的使用心得在android程序中一定不能阻塞UI线程,否则很容易就会弹出norespond的对话框,导致程序退出。为了避免这种情况,一般需要较长时间执行的任务都......
  • 【中级软考—软件设计师】17程序设计语言与语言处理程序基础17.2 编译程序基本原理【*
    编译程序基本原理七、数据类型与程序控制结构八、程序语言基础—表达式前缀表达式一般不涉及一般考中缀和后缀的转换上述求后缀式第一种方法先画图用后序遍历排序上述求后缀......
  • 软件工程心得体会
    通过上学期学过的软件工程科目和这学期课程的初步了解才让我真正意识到我们软件工程这门专业真正需要掌握的具体是什么。我一直以为软件工程重在编程技术的提升,通过编程水......
  • 我的软件工程心得体会1
     在软件工程构建之法这本书中,邹欣写的实践——设计有实际意义的软件工程作业解决了我学习过程中的一些困惑,看到他所说的全国大学生交上来的成千上万的“图书馆信息系统”......