文章目录
- 控制结构
- if-else
- switch
- for(range)
- break和continue
- 标签与goto
- 函数
- 参数与返回值
- 传递变长参数
- defer
- 内置函数
- 递归函数
- 函数作为参数
- 闭包
- 计算函数的执行时间
控制结构
if-else
if condition{
//do something
}
if condition{
//do something
}else{
//do something
}
if condition1{
//do something
}else if condition2{
//do something
}else{
//cathc-all or default
}
//else-if分支没有数量限制,但为了代码的可读性,不要在if后面加入太多的else-if结构。
//当if结构中有break、continue、goto或return语句时,常省略else部分
if condition{
return x
}
return y
//不要同时在if-else结构的两个分支里都使用return语句,这会导致编译错误
//判断go程序的操作系统类型
if runtime.GOOS == "windows"{
...
}else{//unix-like
...
}
if可以包含一个初始化语句,比如给一个变量赋值,具体格式如下:
if initialization; condition{
// do something
}
val:=10
if val>max{
//...
}
//可以写成
//使用:=声明的变量作用域只存在于if结构中
if val:=10; val>max{
//...
}
if value:=process(data); value>max{
...
}
strconv.Atoi可以将一个字符串转换为一个整数
anInt, _ = strconv.Atoi(origStr)
如果不能被转换为整数,anInt的值会变成0,而_忽视了错误,程序会继续运行,正确的做法是检查错误
package main
import (
"fmt"
"strconv"
)
func main(){
var orig string="ABC"
var newS string
fmt.Printf("The size of ints is: %d\n",strconv.IntSize)
an,err:=strconv.Atoi(orig)
if err!=nil{
fmt.Printf("orig %s is not an integer - exiting with error\n",orig)
return
}
fmt.Printf("The integer is %d\n",an)
an=an+5
newS=strconv.Itoa(an)
fmt.Printf("The new string is :%s\n",newS)
}
//习惯用法
value,err:=pack1.function1(param1)
if err!=nil{
fmt.Printf("An error occured in pack1.function1 with parameter %v", param1)
return err
}
//如果想在错误发生的同时终止程序运行,可以使用os包的Exit函数:
if err!=nil{
fmt.Printf("Program stopping with err %v",err)
os.Exit(1)
}
//推出代码1可以使用外部脚本获取到
switch
switch var1 {
case val1:
...
case val2:
...
default:
...
}
- 每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。
- 一旦成功地匹配到某个分支,在执行完相应代码后就会退出整个 switch 代码块,不需要特别使用 break 语句来表示结束。
- 程序也不会自动地去执行下一个分支的代码。如果在执行完每个分支的代码后,还希望继续执行后续分支的代码,可以使用
fallthrough
关键字来达到目的。
switch i {
case 0: // 空分支,只有当 i == 0 时才会进入分支
case 1:
f() // 当 i == 0 时函数不会被调用
}
switch i {
case 0: fallthrough
case 1:
f() // 当 i == 0 时函数也会被调用
}
switch {
case condition1:
...
case condition2:
...
default:
...
}
switch initialization {
case val1:
...
case val2:
...
default:
...
}
//示例
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
}
另外还有, select结构,用于channel选择,后续会提到
for(range)
for 初始化语句; 条件语句; 修饰语句 {}
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 {}
break和continue
一个 break 的作用范围为该语句出现后的最内部的结构,它可以被用于任何形式的 for 循环(计数器、条件判断等)。但在 switch 或 select 语句中,break 语句的作用结果是跳过整个代码块,执行后续的代码。
关键字 continue 忽略剩余的循环体而直接进入下一次循环的过程,但不是无条件执行下一次循环,执行之前依旧需要满足循环的判断条件。
标签与goto
for、switch 或 select 语句都可以配合标签(label)形式的标识符使用,即某一行第一个以冒号(:)结尾的单词
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)
}
}
}
package main
func main() {
i:=0
HERE:
print(i)
i++
if i==5 {
return
}
goto HERE
}
函数
三种类型的函数:
- 普通的带名字的函数
- 匿名函数或者lambda函数
- 方法(Method)
除了main()、init()函数外,其他所有类型的函数都可以有参数与返回值。函数参数、返回值以及它们的类型被统称为函数签名。
package main
func main(){
greeting()
}
func greeting(){
println("In greeting: Hi!!!")
}
参数与返回值
接收参数,返回零个或多个值
通过return关键字返回一组值。
有返回值的函数都必须以return或panic结尾
return之后的语句都不会被执行
函数定义时,形参一般是有名字的,也可以定义没有形参名的函数,只有相应的形参类型:func f(int, int, float64)
没有参数的函数通常被称为niladic函数,就像mian.main()
按值传递call by value、按引用传递call by reference
go默认使用值传递来传递参数,也就是传递参数的副本。对传递的值进行修改,不会影响原来的变量。Function(arg1)
如果希望函数可以直接修改参数的值,而不是对副本进行操作,需要将参数的地址传递给函数(变量名前加&符号),这就是按引用传递。Function(&arg1)。此时传递给函数的是一个指针,指针的值(一个地址)会被复制,但指针的值所指向的地址上的值不会被复制;通过这个指针的值来修改这个值所指向的地址上的值。
传递指针的消耗比传递副本来的少。
在函数调用时,切片、字典、接口、通道这样的引用类型都默认使用引用传递(即使没有显式的指出指针)
package main
import "fmt"
func main() {
fmt.Printf("Multiply 2 * 5 * 6 = %d\n", MultiPly3Nums(2, 5, 6))
// var i1 int = MultiPly3Nums(2, 5, 6)
// fmt.Printf("MultiPly 2 * 5 * 6 = %d\n", i1)
}
func MultiPly3Nums(a int, b int, c int) int {
// var product int = a * b * c
// return product
return a * b * c
}
//Multiply 2 * 5 * 6 = 60
命名的返回值
当需要返回多个非命名返回值时,需要使用 () 把它们括起来,比如 (int, int)
命名返回值作为结果形参(result parameters)被初始化为相应类型的零值,当需要返回的时候,我们只需要一条简单的不带参数的 return 语句。需要注意的是,即使只有一个命名返回值,也需要使用 () 括起来
package main
import "fmt"
var num int = 10
var numx2, numx3 int
func main() {
numx2, numx3 = getX2AndX3(num)
PrintValues()
numx2, numx3 = getX2AndX3_2(num)
PrintValues()
}
func PrintValues() {
fmt.Printf("num = %d, 2x num = %d, 3x num = %d\n", num, numx2, numx3)
}
func getX2AndX3(input int) (int, int) {
return 2 * input, 3 * input
}
func getX2AndX3_2(input int) (x2 int, x3 int) {
x2 = 2 * input
x3 = 3 * input
// return x2, x3
return
}
空白符
package main
import "fmt"
func main() {
var i1 int
var f1 float32
i1, _, f1 = ThreeValues()
fmt.Printf("The int: %d, the float: %f \n", i1, f1)
}
func ThreeValues() (int, int, float32) {
return 5, 6, 7.5
}
改变外部变量
传递指针,赋予了函数直接修改外部变量的能力,所以被修改的变量不再需要使用return返回
package main
import (
"fmt"
)
// this function changes reply:
func Multiply(a, b int, reply *int) {
*reply = a * b
}
func main() {
n := 0
reply := &n
Multiply(10, 5, reply)
fmt.Println("Multiply:", *reply) // Multiply: 50
}
传递变长参数
如果函数的最后一个参数是采用...type
的形式,函数就可以处理一个变长的参数,这个长度可以为0,这样的函数称为变长函数。
func myFunc(a,b,arg ....int){}
func Greeting(prefix string, who ...string)
Greeting("hello:","joe","anna","eileen")
如果参数被存储在一个slice类型的变量中,可以通过slice… 的形式来传递参数调用变参函数
package main
import "fmt"
func main() {
x := min(1, 3, 2, 0)
fmt.Printf("The minimum is: %d\n", x)
slice := []int{7,9,3,5,1}
x = min(slice...)
fmt.Printf("The minimum in the slice is: %d", x)
}
func min(s ...int) int {
if len(s)==0 {
return 0
}
min := s[0]
for _, v := range s {
if v < min {
min = v
}
}
return min
}
//The minimum is: 0
//The minimum in the slice is: 1
defer
关键字defer允许推迟到函数返回之前(或者任意位置执行return语句之后)一刻才执行某个语句或函数
defer和return的执行顺序是先返回值赋值,然后执行defer,然后return到函数调用处
package main
import "fmt"
func main() {
function1()
}
func function1() {
fmt.Printf("In function1 at the top\n")
defer function2()
fmt.Printf("In function1 at the bottom!\n")
}
func function2() {
fmt.Printf("function2: Deferred until the end of the calling function!")
}
//In Function1 at the top
//In Function1 at the bottom!
//Function2: Deferred until the end of the calling function!
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
//打印0
有多个defer行为被注册时,会以逆序执行
func f() {
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
}
// 4 3 2 1 0
defer允许进行一些函数执行完成后的收尾工作:
1、关闭文件流
defer file.Close()
2、解锁加密资源
mu.Lock()
defer mu.Unlock()
3、打印最终报告
printHeader()
defer printFooter()
4、关闭数据库连接
defer disconnectFromDB()
使用defer语句实现代码追踪
package main
import "fmt"
func trace(s string) { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }
func a() {
trace("a")
defer untrace("a")
fmt.Println("in a")
}
func b() {
trace("b")
defer untrace("b")
fmt.Println("in b")
a()
}
func main() {
b()
}
//entering: b
//in b
//entering: a
//in a
//leaving: a
//leaving: b
package main
import "fmt"
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func a() {
defer un(trace("a"))
fmt.Println("in a")
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}
func main() {
b()
}
内置函数
递归函数
package main
import "fmt"
func main() {
result := 0
for i := 0; i <= 10; i++ {
result = fibonacci(i)
fmt.Printf("fibonacci(%d) is: %d\n", i, result)
}
}
func fibonacci(n int) (res int) {
if n <= 1 {
res = 1
} else {
res = fibonacci(n-1) + fibonacci(n-2)
}
return
}
函数作为参数
函数可以作为其他函数进行传递,然后在其他函数内调用,一般称之为回调。
package main
import (
"fmt"
)
func main() {
callback(1, Add)
}
func Add(a, b int) {
fmt.Printf("The sum of %d and %d is: %d\n", a, b, a+b)
}
func callback(y int, f func(int, int)) {
f(y, 2) // this becomes Add(1, 2)
}
//The sum of 1 and 2 is: 3
闭包
当不希望给函数起名字的时候,可以使用匿名函数,func(x, y int) int{ return x+y }
这样的函数不能独立存在,但可以被赋予某个变量,即保存函数的地址到变量中:fplus:=func(x,y int) int {return x+y},然后通过变量名对函数进行调用:fplus(3,4)
也可以直接对匿名函数进行调用:func(x,y int) int {return x+y}(3,4)
func() {
sum := 0
for i := 1; i <= 1e6; i++ {
sum += i
}
}()
//花括号 {} 涵盖着函数体,最后的一对括号表示对该匿名函数的调用。
defer语句和匿名函数
关键字defer经常配合匿名函数使用,可以用于改变函数的命名返回值。
匿名函数同样被称之为闭包,允许调用定义在其他环境中的变量。
函数作为返回值
package main
import "fmt"
func main() {
// make an Add2 function, give it a name p2, and call it:
p2 := Add2()
fmt.Printf("Call Add2 for 3 gives: %v\n", p2(3))
// make a special Adder function, a gets value 2:
TwoAdder := Adder(2)
fmt.Printf("The result is: %v\n", TwoAdder(3))
}
func Add2() func(b int) int {
return func(b int) int {
return b + 2
}
}
func Adder(a int) func(b int) int {
return func(b int) int {
return a + b
}
}
//Call Add2 for 3 gives: 5
//The result is: 5
package main
import "fmt"
func main() {
var f = Adder()
fmt.Print(f(1), " - ")
fmt.Print(f(20), " - ")
fmt.Print(f(300))
}
func Adder() func(int) int {
var x int
return func(delta int) int {
x += delta
return x
}
}
//1 - 21 - 321
在多次调用中,变量 x 的值是被保留的,即 0 + 1 = 1,然后 1 + 20 = 21,最后 21 + 300 = 321:闭包函数保存并积累其中的变量的值,不管外部函数退出与否,它都能够继续操作外部函数中的局部变量。
计算函数的执行时间
time包的Now()和Sub函数
start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf("longCalculation took this amount of time: %s\n", delta)