今儿继续学go,争取多学点吧,不过感觉困死了,不一定能学多少。
循环语句 惊奇的发现go是没有while循环的,全靠for实现
for循环 一共三种形式
for init; condition; post {}
for condition {}
for {}
第一种就是和C差不多的,初始化;条件;后置语句,用来控制循环的次数
第二种类似于while
第三种用来无限循环,直到遇到break
for循环还能对map、slice、数组、字符串进行迭代循环,语法如下
1 2 3 for key, value := range oldMap { newMap[key] = value }
其中key和value都是可以省略的,省略的话就只会迭代key,或者只会迭代value
如果想只读key
如果想只读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 for 和 switch 都可以用
唯一需要注意啊的是break和别的不同的一点是可以添加label也就是标签。使用时可以添加在最前面就比如下面这个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func main () { outerLoop: for i := 0 ; i < 3 ; i++ { for j := 0 ; j < 5 ; j++ { if j == 3 { break 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 mainimport ( "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" }() 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 mainimport "fmt" func main () { 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 mainimport "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 mainimport "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 = y y = temp return temp; }
最终如果调用函数,则函数内将交换值,但全局变量并不会变。
引用传递值 这里用到指针进行操作,由于指针指向的是实际存储地址,所以在交换后将直接对变量所指向的地址进行改变,进而改变变量值
1 2 3 4 5 6 7 func swap (x *int , y *int ) { var temp int temp = *x *x = *y *y = temp }
函数作为实参 go中可以将函数直接传递给某个变量而作为实参
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport ( "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 mainimport "fmt" func getSequence () func () int { i:=0 return func () int { i+=1 return i } }func main () { nextNumber := getSequence() fmt.Println(nextNumber()) fmt.Println(nextNumber()) fmt.Println(nextNumber()) nextNumber1 := getSequence() fmt.Println(nextNumber1()) fmt.Println(nextNumber1()) }
方法 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 mainimport "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) }
在这个例子中,Area 方法与 Rectangle 类型关联。当调用 rect.Area() 时,rect 作为接收者被传递给 Area 方法,方法内部使用 r.Width 和 r.Height 计算面积。
总结
方法是与特定类型关联的函数。
方法的第一个参数是接收者,可以是值类型或指针类型。
方法调用时,会将接收者作为第一个参数传递给方法函数。
方法函数会根据接收者的值或指针进行计算。
变量作用域 作用域控制代码中各种元素的使用范围,如包、结构体、变量、函数、方法、常量
具体来看可以分成三个
局部变量——函数级作用域
全局变量——包级作用域
块级作用域
形参变量——函数参数
局部变量 函数内定义的变量称为局部变量,参数和返回值均为局部变量
变量用var定义
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "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 mainimport "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 mainfunc myFunc () { if true { blockVar := 30 } }
今天就到这里吧,虽然但是,总感觉最近学习状态好差,得想办法调整调整了。