go学习记录——第二天

今儿继续学go,争取多学点吧,不过感觉困死了,不一定能学多少。

循环语句

惊奇的发现go是没有while循环的,全靠for实现

for循环

一共三种形式

  1. for init; condition; post {}
  2. for condition {}
  3. for {}

第一种就是和C差不多的,初始化;条件;后置语句,用来控制循环的次数

第二种类似于while

第三种用来无限循环,直到遇到break

for循环还能对map、slice、数组、字符串进行迭代循环,语法如下

1
2
3
for key, value := range oldMap {
newMap[key] = value
}

其中key和value都是可以省略的,省略的话就只会迭代key,或者只会迭代value

如果想只读key

1
for key := range oldMap

如果想只读value

1
for _, value := range oldMap

循环嵌套

和别的一样

1
2
3
4
5
6
7
8
for [condition |  ( init; condition; increment ) | Range]
{
for [condition | ( init; condition; increment ) | Range]
{
statement(s)
}
statement(s)
}

循环控制

break

forswitch 都可以用

唯一需要注意啊的是break和别的不同的一点是可以添加label也就是标签。使用时可以添加在最前面就比如下面这个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
outerLoop: // 定义一个名为 outerLoop 的标签
for i := 0; i < 3; i++ {
for j := 0; j < 5; j++ {
if j == 3 {
break outerLoop // 跳出 outerLoop 标签的循环
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
fmt.Println("循环结束")
}

这里用outerLoop标记了两层循环,最后跳出循环后继续走下面的代码

switch和select都可以用break,但通常都用于最后一个case。但select中如果想使用break,虽然可以但理论上由于是非阻塞式循环,是在所有通道匹配后才会输出,break就会影响这个效果。所以通常使用return或goto来跳出循环。

但这里还有一个问题,由于goto是直接直接去到标记的位置,所以对select来说其实会影响代码的可读性,所以通常还是用return。关于goto后面还会有内容。

return的例子如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"
"time"
)

func main() {
c1 := make(chan int)
c2 := make(chan string)

go func() {
time.Sleep(time.Second)
c1 <- 1
}()

go func() {
time.Sleep(2 * time.Second)
c2 <- "hello"
}()

// 使用 return 跳出 select 语句
for {
select {
case v := <-c1:
fmt.Println("received from c1:", v)
return // 退出函数
case v := <-c2:
fmt.Println("received from c2:", v)
return // 退出函数
}
}
}

continue

和别的没什么区别

唯一需要注意的不同就是label

go的continue也可以使用label去进行操作,例子如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
outerLoop: // 定义一个名为 outerLoop 的标签
for i := 0; i < 3; i++ {
for j := 0; j < 5; j++ {
if j == 3 {
continue outerLoop // 跳过当前外层循环的剩余迭代
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
fmt.Println("循环结束")
}

goto

goto就像前面说的,无条件直达某个位置,这也就导致了代码的结构性和可读性变差,所以能不用就不用

1
2
3
4
goto label;
..
.
label: statement;

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
/* 定义局部变量 */
var a int = 10

/* 循环 */
LOOP: for a < 20 {
if a == 15 {
/* 跳过迭代 */
a = a + 1
goto LOOP
}
fmt.Printf("a的值为 : %d\n", a)
a++
}
}

无限循环

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
for true {
fmt.Printf("这是无限循环。\n");
}
}

函数

和别的一样,概念没啥好说的

函数格式

1
2
3
func function_name( [parameter list] ) [return_types] {
函数体
}

func:函数从func开始声明
function_name:函数名称
parameter list:参数列表
return_types:返回类型,可以省略
示例

1
2
3
4
5
6
7
8
9
10
11
12
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 声明局部变量 */
var result int

if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}

函数调用和python没区别,一个道理

函数参数

函数参数依旧是形参非实参

例子

1
2
3
4
5
6
7
8
9
10
/* 定义相互交换值的函数 */
func swap(x, y int) int {
var temp int

temp = x /* 保存 x 的值 */
x = y /* 将 y 值赋给 x */
y = temp /* 将 temp 值赋给 y*/

return temp;
}

最终如果调用函数,则函数内将交换值,但全局变量并不会变。

引用传递值

这里用到指针进行操作,由于指针指向的是实际存储地址,所以在交换后将直接对变量所指向的地址进行改变,进而改变变量值

1
2
3
4
5
6
7
/* 定义交换值函数*/
func swap(x *int, y *int) {
var temp int
temp = *x /* 保持 x 地址上的值 */
*x = *y /* 将 y 值赋给 x */
*y = temp /* 将 temp 值赋给 y */
}

函数作为实参

go中可以将函数直接传递给某个变量而作为实参

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
"math"
)

func main(){
/* 声明函数变量 */
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
}

/* 使用函数 */
fmt.Println(getSquareRoot(9))

}

闭包(匿名函数)

匿名函数是一个“内联”语句或表达式,优点在于可以直接使用函数内的变量,不用声明。

匿名函数是无需命名的函数,可以被赋值给变量,方便后续调用。它们可以访问定义时的外部变量,但只访问了这些变量的副本,不会改变外部变量的值。

当调用匿名函数并传递参数时,参数会覆盖函数内部的同名变量,函数执行完毕后,这些参数会恢复到初始状态。

匿名函数的优势在于可以方便地创建临时函数,避免重复代码。

闭包是匿名函数的一个重要特性,它允许匿名函数访问外部变量,即使外部函数已经执行完毕。这里访问的是外部变量的初始参数,而不是副本。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import "fmt"

func getSequence() func() int {
i:=0
return func() int {
i+=1
return i
}
}

func main(){
/* nextNumber 为一个函数,函数 i 为 0 */
nextNumber := getSequence()

/* 调用 nextNumber 函数,i 变量自增 1 并返回 */
fmt.Println(nextNumber())
fmt.Println(nextNumber())
fmt.Println(nextNumber())

/* 创建新的函数 nextNumber1,并查看结果 */
nextNumber1 := getSequence()
fmt.Println(nextNumber1())
fmt.Println(nextNumber1())
}
/*
output:
1
2
3
1
2
*/

方法

go中除了函数还有方法,是与特定类型(结构体、接口等)关联的函数,将接收者作为函数的第一个参数。接收者可以是值类型(值拷贝)或指针类型(指针引用)。方法调用时,会将接收者作为第一个参数传递给方法函数,然后方法函数会根据接收者的值或指针进行计算。

方法的声明格式如下

1
2
3
func (variable_name variable_data_type) function_name() [return_type]{
/* 函数体*/
}

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

type Rectangle struct {
Width int
Height int
}

// 方法:计算矩形的面积
func (r Rectangle) Area() int {
return r.Width * r.Height
}

func main() {
rect := Rectangle{Width: 10, Height: 5}
area := rect.Area() // 调用方法
fmt.Println("面积:", area) // 输出:面积: 50
}

在这个例子中,Area 方法与 Rectangle 类型关联。当调用 rect.Area() 时,rect 作为接收者被传递给 Area 方法,方法内部使用 r.Widthr.Height 计算面积。

总结

  • 方法是与特定类型关联的函数。
  • 方法的第一个参数是接收者,可以是值类型或指针类型。
  • 方法调用时,会将接收者作为第一个参数传递给方法函数。
  • 方法函数会根据接收者的值或指针进行计算。

变量作用域

作用域控制代码中各种元素的使用范围,如包、结构体、变量、函数、方法、常量

具体来看可以分成三个

  • 局部变量——函数级作用域
  • 全局变量——包级作用域
  • 块级作用域
  • 形参变量——函数参数

局部变量

函数内定义的变量称为局部变量,参数和返回值均为局部变量

变量用var定义

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
/* 声明局部变量 */
var a, b, c int

/* 初始化参数 */
a = 10
b = 20
c = a + b

fmt.Printf ("结果: a = %d, b = %d and c = %d\n", a, b, c)
}

全局变量

函数外定义的变量称为全局变量全局变量可以在整个包(甚至包导出后)使用

变量用var定义,在包的顶部定义

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

/* 声明全局变量 */
var g int

func main() {

/* 声明局部变量 */
var a, b int

/* 初始化参数 */
a = 10
b = 20
g = a + b

fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g)
}

在整个代码中,全局变量和局部变量的名称可以是一样的,但在函数内优先使用局部变量。这个和别的是一样的

形参变量

形参变量只能在函数内使用,一般作为函数内的局部变量使用

块级作用域

在代码块(for, if, switch 等)中,变量的作用范围为这个代码块。

例子

1
2
3
4
5
6
7
package main

func myFunc() {
if true {
blockVar := 30 // 块级作用域
}
}

今天就到这里吧,虽然但是,总感觉最近学习状态好差,得想办法调整调整了。


go学习记录——第二天
https://www.lx02918.ltd/2024/08/08/go-study-second-day/
作者
Seth
发布于
2024年8月8日
许可协议