海报模板在线制作免费网站免费发布信息网站网址大全
- 作者: 五速梦信息网
- 时间: 2026年04月20日 11:01
当前位置: 首页 > news >正文
海报模板在线制作免费网站,免费发布信息网站网址大全,陕西工程项目信息网,做儿童文学有哪些的网站控制结构
if 的“快乐路径”原则
针对程序的分支结构#xff0c;Go 提供了 if 和 switch-case 两种语句形式#xff1b;而针对循环结构#xff0c;Go 只保留了 for 这一种循环语句形式。
if 语句
if 语句是 Go 语言中提供的一种分支控制结构#xff0c;它也是 Go 中最常…控制结构
if 的“快乐路径”原则
针对程序的分支结构Go 提供了 if 和 switch-case 两种语句形式而针对循环结构Go 只保留了 for 这一种循环语句形式。
if 语句
if 语句是 Go 语言中提供的一种分支控制结构它也是 Go 中最常用、最简单的分支控制结构。它会根据布尔表达式的值在两个分支中选择一个执行。虽然各种编程语言几乎都原生支持了 if 语句但 Go 的 if 语句依然有着自己的特点 第一和 Go 函数一样if 语句的分支代码块的左大括号与 if 关键字在同一行上这也是 Go 代码风格的统一要求gofmt 工具会帮助我们实现这一点第二if 语句的布尔表达式整体不需要用括号包裹一定程度上减少了开发人员敲击键盘的次数。而且if 关键字后面的条件判断表达式的求值结果必须是布尔类型即要么是 true要么是 false。如果判断的条件比较多我们可以用多个逻辑操作符连接起多个条件判断表达式比如这段代码就是用了多个逻辑操作符 来连接多个布尔表达式if (runtime.GOOS linux) (runtime.GOARCH amd64)
(runtime.Compiler ! gccgo) {println(we are using standard go compiler on linux os for amd64)
}Go 语言的 if 语句还有其他多种形式比如二分支结构和多N分支结构。多分支结构引入了 else if。
支持声明 if 语句的自用变量
无论是单分支、二分支还是多分支结构我们都可以在 if 后的布尔表达式前进行一些变量的声明在 if 布尔表达式前声明的变量称为 if 语句的自用变量。顾名思义这些变量只可以在 if 语句的代码块范围内使用。在 if 语句中声明自用变量是 Go 语言的一个惯用法这种使用方式直观上可以让开发者有一种代码行数减少的感觉提高可读性。同时由于这些变量是 if 语句自用变量它的作用域仅限于 if 语句的各层隐式代码块中if 语句外部无法访问和更改这些变量这就让这些变量具有一定隔离性这样你在阅读和理解 if 语句的代码时也可以更聚焦。
if 语句的“快乐路径”原则
从可读性上来看单分支结构要优于二分支结构二分支结构又优于多分支结构。那么显然我们在日常编码中要减少多分支结构甚至是二分支结构的使用这会有助于我们编写出优雅、简洁、易读易维护且不易错的代码。if 语句的“快乐路径Happy Path”原则 仅使用单分支控制结构当布尔表达式求值为 false 时也就是出现错误时在单分支中快速返回正常逻辑在代码布局上始终“靠左”这样读者可以从上到下一眼看到该函数正常逻辑的全貌函数执行到最后一行代表一种成功状态。
Go 中的 switch 语句
认识 switch 语句
除了 if 语句之外Go 语言还提供了一种更适合多路分支执行的分支控制结构也就是 switch 语句。在一些执行分支较多的场景下使用 switch 分支控制语句可以让代码更简洁可读性更好。Go 语言中 switch 语句的一般形式switch initStmt; expr {case expr1:// 执行分支1case expr2:// 执行分支2case expr3_1, expr3_2, expr3_3:// 执行分支3case expr4:// 执行分支4… …case exprN:// 执行分支Ndefault:// 执行默认分支
}首先看这个 switch 语句一般形式中的第一行这一行由 switch 关键字开始它的后面通常接着一个表达式expr这句中的 initStmt 是一个可选的组成部分。我们可以在 initStmt 中通过短变量声明定义一些在 switch 语句中使用的临时变量。接下来switch 后面的大括号内是一个个代码执行分支每个分支以 case 关键字开始每个 case 后面是一个表达式或是一个逗号分隔的表达式列表。这里还有一个以 default 关键字开始的特殊分支被称为默认分支。最后我们再来看 switch 语句的执行流程。switch 语句会用 expr 的求值结果与各个 case 中的表达式结果进行比较如果发现匹配的 case也就是 case 后面的表达式或者表达式列表中任意一个表达式的求值结果与 expr 的求值结果相同那么就会执行该 case 对应的代码分支分支执行后switch 语句也就结束了。如果所有 case 表达式都无法与 expr 匹配那么程序就会执行 default 默认分支并且结束 switch 语句。Go 先对 switch expr 表达式进行求值然后再按 case 语句的出现顺序从上到下进行逐一求值。在带有表达式列表的 case 语句中Go 会从左到右对列表中的表达式进行求值。
switch 语句的灵活性
C 语言中的 switch 语句对表达式类型有限制每个 case 语句只可以有一个表达式。而且除非你显式使用 break 跳出程序默认总是执行下一个 case 语句。这些特性开发人员带来了使用上的心智负担。相较于 C 语言中 switch 语句的“死板”Go 的 switch 语句表现出极大的灵活性主要表现在如下几方面 首先switch 语句各表达式的求值结果可以为各种类型值只要它的类型支持比较操作就可以了。 C 语言中switch 语句中使用的所有表达式的求值结果只能是 int 或枚举类型其他类型都会被 C 编译器拒绝。Go 语言就宽容得多了只要类型支持比较操作都可以作为 switch 语句中的表达式类型。比如整型、布尔类型、字符串类型、复数类型、元素类型都是可比较类型的数组类型甚至字段类型都是可比较类型的结构体类型也可以。type person struct {name stringage int
}
func main() {p : person{tom, 13}switch p {case person{tony, 33}:println(match tony)case person{tom, 13}:println(match tom)case person{lucy, 23}:println(match lucy)default:println(no match)}
}第二点switch 语句支持声明临时变量。第三点case 语句支持表达式列表。第四点取消了默认执行下一个 case 代码逻辑的语义。
type switch
type switch”这是一种特殊的 switch 语句用法func main() {var x interface{} 13switch x.(type) {case nil:println(x is nil)case int:println(the type of x is int)case string:println(the type of x is string)case bool:println(the type of x is string)default:println(dont support the type)}
}switch 关键字后面跟着的表达式为 x.(type)这种表达式形式是 switch 语句专有的而且也只能在 switch 语句中使用。这个表达式中的 x 必须是一个接口类型变量表达式的求值结果是这个接口类型变量对应的动态类型。接着case 关键字后面接的就不是普通意义上的表达式了而是一个个具体的类型。这样Go 就能使用变量 x 的动态类型与各个 case 中的类型进行匹配之后的逻辑就都是一样的了。 Go 语言规范中明确规定不带 label 的 break 语句中断执行并跳出的是同一函数内 break 语句所在的最内层的 for、switch 或 select。
Go 的 for 循环
for 语句的经典使用形式
主流编程语言都提供了对循环结构的支持绝大多数主流语言包括 C 语言、C、Java 和 Rust甚至连动态语言 Python 还提供了不止一种的循环语句但 Go 却只有一种也就是 for 语句。Go 语言中 for 循环语句的经典形式var sum int
for i : 0; i 10; i {sum i
}
println(sum)图中①对应的组成部分执行于循环体③ 之前并且在整个 for 循环语句中仅会被执行一次它也被称为循环前置语句。 我们通常会在这个部分声明一些循环体③ 或循环控制条件② 会用到的自用变量也称循环变量或迭代变量比如这里声明的整型变量 i。与 if 语句中的自用变量一样for 循环变量也采用短变量声明的形式循环变量的作用域仅限于 for 语句隐式代码块范围内。 图中②对应的组成部分是用来决定循环是否要继续进行下去的条件判断表达式。 和 if 语句的一样这个用于条件判断的表达式必须为布尔表达式如果有多个判断条件我们一样可以由逻辑操作符进行连接。当表达式的求值结果为 true 时代码将进入循环体③继续执行相反则循环直接结束循环体③与组成部分④都不会被执行。 图中③对应的组成部分是 for 循环语句的循环体。 如果相关的判断条件表达式求值结构为 true 时循环体就会被执行一次这样的一次执行也被称为一次迭代Iteration。在上面例子中循环体执行的动作是将这次迭代中变量 i 的值累加到变量 sum 中。 图中④对应的组成部分会在每次循环体迭代之后执行也被称为循环后置语句。 这个部分通常用于更新 for 循环语句组成部分①中声明的循环变量比如在这个例子中我们在这个组成部分对循环变量 i 进行加 1 操作。 Go 语言的 for 循环支持声明多循环变量并且可以应用在循环体以及判断条件中for i, j, k : 0, 1, 2; (i 20) (j 10) (k 30); i, j, k i1, j1,sum (i j k)println(sum)
}除了循环体部分之外其余的三个部分都是可选的。虽然对前置语句或后置语句进行了省略但经典 for 循环形式中的分号依然被保留着这是 Go 语法的要求。当循环前置与后置语句都省略掉仅保留循环判断条件表达式时我们可以省略经典 for 循环形式中的分号。当 for 循环语句的循环判断条件表达式的求值结果始终为 true 时我们就可以将它省略掉了。这个 for 循环就是我们通常所说的“无限循环”。
for range 循环形式
针对像切片这样的复合数据类型还有 Go 原生的字符串类型stringGo 语言提供了一个更方便的“语法糖”形式for range。var sl []int{1, 2, 3, 4, 5}
for i, v : range sl {fmt.Printf(sl[%d] %d\n, i, v)
}for range 循环形式与 for 语句经典形式差异较大除了循环体保留了下来其余组成部分都“不见”了。其实那几部分已经被融合到 for range 的语义中了。这里的 i 和 v 对应的是经典 for 语句形式中循环前置语句的循环变量它们的初值分别为切片 sl 的第一个元素的下标值和元素值。隐含在 for range 语义中的循环控制条件判断为是否已经遍历完 sl 的所有元素等价于i len(sl)这个布尔表达式。每次迭代后for range 会取出切片 sl 的下一个元素的下标和值分别赋值给循环变量 i 和 v这与 for 经典形式下的循环后置语句执行的逻辑是相同的。 for range 语句也有几个常见“变种” 变种一当我们不关心元素的值时我们可以省略代表元素值的变量 v只声明代表下标值的变量 i。变种二如果我们不关心元素下标只关心元素值那么我们可以用空标识符替代代表下标值的变量 i。这里一定要注意这个空标识符不能省略否则就与上面的“变种一”形式一样了Go 编译器将无法区分。变种三如果我们既不关心下标值也不关心元素值可以将 iv 都省略。
string 类型
for range 对于 string 类型来说每次循环得到的 v 值是一个 Unicode 字符码点也就是 rune 类型值而不是一个字节返回的第一个值 i 为该 Unicode 字符码点的内存编码UTF-8的第一个字节在字符串内存序列中的位置。使用 for 经典形式与使用 for range 形式对 string 类型进行循环操作的语义是不同的。
map
map 就是一个键值对key-value集合最常见的对 map 的操作就是通过 key 获取其对应的 value 值。但有些时候我们也要对 map 这个集合进行遍历这就需要 for 语句的支持了。但在 Go 语言中我们要对 map 进行循环操作for range 是唯一的方法for 经典循环形式是不支持对 map 类型变量的循环控制的。for range 对于 map 类型来说每次循环循环变量 k 和 v 分别会被赋值为 map 键值对集合中一个元素的 key 值和 value 值。
channel
channel 是 Go 语言提供的并发设计的原语它用于多个 Goroutine 之间的通信。当 channel 类型变量作为 for range 语句的迭代对象时for range 会尝试从 channel 中读取数据使用形式是这样的var c make(chan int)
for v : range c {// …
}for range 每次从 channel 中读取一个元素后会把它赋值给循环变量 v并进入循环体。当 channel 中没有数据可读的时候for range 循环会阻塞在对 channel 的读操作上。直到 channel 关闭时for range 循环才会结束这也是 for range 循环与 channel 配合时隐含的循环判断条件。
带 label 的 continue 语句
日常开发中出于算法逻辑的需要我们可能会有中断当前循环体并继续下一次迭代的时候也会有中断循环体并彻底结束循环语句的时候。针对这些情况Go 语言提供了 continue 语句和 break 语句。 如果循环体中的代码执行到一半要中断当前迭代忽略此迭代循环体中的后续代码并回到 for 循环条件判断尝试开启下一次迭代这个时候我们可以使用 continue 语句来应对。Go 语言中的 continue 在 C 语言 continue 语义的基础上又增加了对 label 的支持。 label 语句的作用是标记跳转的目标。func main() {var sum intvar sl []int{1, 2, 3, 4, 5, 6}
loop:for i : 0; i len(sl); i {if sl[i]%2 0 {// 忽略切片中值为偶数的元素continue loop}sum sl[i]}println(sum) // 9
}在这段代码中我们定义了一个 labelloop它标记的跳转目标恰恰就是我们的 for 循环。也就是说我们在循环体中可以使用 continue loop label 的方式来实现循环体中断。通常我们在这样非嵌套循环的场景中会直接使用不带 label 的 continue 语句。带 label 的 continue 语句通常出现于嵌套循环语句中被用于跳转到外层循环并继续执行外层循环语句的下一个迭代func main() {var sl [][]int{{1, 34, 26, 35, 78},{3, 45, 13, 24, 99},{101, 13, 38, 7, 127},{54, 27, 40, 83, 81},}
outerloop:for i : 0; i len(sl); i {for j : 0; j len(sl[i]); j {if sl[i][j] 13 {fmt.Printf(found 13 at [%d, %d]\n, i, j)continue outerloop}}}
}break 语句的使用
无论带不带 labelcontinue 语句的本质都是继续循环语句的执行。但日常编码中我们还会遇到一些场景在这些场景中我们不仅要中断当前循环体迭代的进行还要同时彻底跳出循环终结整个循环语句的执行。面对这样的场景continue 语句就不再适用了Go 语言为我们提供了 break 语句来解决这个问题。和 continue 语句一样Go 也 break 语句增加了对 label 的支持。而且和 continue 语句一样如果遇到嵌套循环break 要想跳出外层循环用不带 label 的 break 是不够因为不带 label 的 break 仅能跳出其所在的最内层循环。要想实现外层循环的跳出我们还需给 break 加上 label。
for 语句的常见“坑”与避坑方法
问题一循环变量的重用 for range 形式的循环语句使用短变量声明的方式来声明循环变量循环体将使用这些循环变量实现特定的逻辑但在使用的时候可能会发现循环变量的值与“预期”不符。有时候循环变量在 for range 语句中仅会被声明一次且在每次迭代中都会被重用。 问题二参与循环的是 range 表达式的副本 在 for range 语句中range 后面接受的表达式的类型可以是数组、指向数组的指针、切片、字符串还有 map 和 channel需具有读权限。func main() {var a [5]int{1, 2, 3, 4, 5}var r [5]intfmt.Println(original a , a)for i, v : range a {if i 0 {a[1] 12a[2] 13}r[i] v} fmt.Println(after for range loop, r , r)fmt.Println(after for range loop, a , a)
}每次迭代的都是从数组 a 的值拷贝 a’ 中得到的元素。a’是 Go 临时分配的连续字节序列与 a 完全不是一块内存区域。因此无论 a 被如何修改它参与循环的副本 a’依旧保持原值因此 v 从 a’中取出的仍旧是 a 的原值而不是修改后的值。 那么应该如何解决这个问题让输出结果符合我们的预期呢 大多数应用数组的场景我们都可以用切片替代func main() {var a [5]int{1, 2, 3, 4, 5}var r [5]intfmt.Println(original a , a)for i, v : range a[:] {if i 0 {a[1] 12a[2] 13}r[i] v} fmt.Println(after for range loop, r , r)fmt.Println(after for range loop, a , a)
}在 range 表达式中我们用了 a[:]替代了原先的 a也就是将数组 a 转换为一个切片作为 range 表达式的循环对象。当进行 range 表达式复制时我们实际上复制的是一个切片也就是表示切片的结构体。表示切片副本的结构体中的 array依旧指向原切片对应的底层数组所以我们对切片副本的修改也都会反映到底层数组 a 上去。而 v 再从切片副本结构体中 array 指向的底层数组中获取数组元素也就得到了被修改后的元素值。 问题三遍历 map 中元素的随机性 如果我们在循环的过程中对 map 进行了修改那么这样修改的结果是否会影响后续迭代呢这个结果和我们遍历 map 一样具有随机性。我们日常编码遇到遍历 map 的同时还需要对 map 进行修改的场景的时候要格外小心。
- 上一篇: 海报模板网站有哪些房地产数据网站
- 下一篇: 海报制作网站免费全栈网站开发流程图
相关文章
-
海报模板网站有哪些房地产数据网站
海报模板网站有哪些房地产数据网站
- 技术栈
- 2026年04月20日
-
还有哪些免费的网站可以做H5wordpress 邀请链接
还有哪些免费的网站可以做H5wordpress 邀请链接
- 技术栈
- 2026年04月20日
-
哈市住房和建设局网站做点阵纸的网站
哈市住房和建设局网站做点阵纸的网站
- 技术栈
- 2026年04月20日
-
海报制作网站免费全栈网站开发流程图
海报制作网站免费全栈网站开发流程图
- 技术栈
- 2026年04月20日
-
海北高端网站建设多少钱北京房产
海北高端网站建设多少钱北京房产
- 技术栈
- 2026年04月20日
-
海北营销网站建设公司2024近期新闻
海北营销网站建设公司2024近期新闻
- 技术栈
- 2026年04月20日
