1.流程控制
1.1 条件语句
if a < 5 { return 0
} else { return 1
}
注意:在有返回值的函数中,不允许将“最终的”return语句包含在if...else...结构中, 否则会编译失败!!!
func example(x int) int { if x == 0 { return 5 } else { return x }
}// 编译失败的原因:Go编译器无法找到终止该函数的return语句
1.2 选择语句
switch i { case 0: fmt.Printf("0") case 1: fmt.Printf("1") case 2: fallthrough case 3: fmt.Printf("3") case 4, 5, 6: fmt.Printf("4, 5, 6") default: fmt.Printf("Default")
}
运行上面的案例,将会得到如下结果: i = 0 时,输出 0 ; i = 1 时,输出 1 ; i = 2 时,输出 3 ; i = 3 时,输出 3 ; i = 4 时,输出 4, 5, 6 ; i = 5 时,输出 4, 5, 6 ; i = 6 时,输出 4, 5, 6 ; i = 其他任意值时,输出 Default 。
比较有意思的是,switch后面的表达式甚至不是必需的,比如下面的例子:
switch { case 0 <= Num && Num <= 3: fmt.Printf("0-3") case 4 <= Num && Num <= 6: fmt.Printf("4-6") case 7 <= Num && Num <= 9: fmt.Printf("7-9")
}
- 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case
- 可以不设定switch之后的条件表达式,在此种情况下,整个switch结构与多个 if...else...的逻辑作用等同
- 在条件表达式中也支持多重赋值
func ForCase() {sum := 0for i := 0; i < 10; i++ {sum += i}fmt.Println(sum) // 45// 在条件表达式中也支持多重赋值,如下表示:a := []int{1, 2, 3, 4, 5, 6}for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {a[i], a[j] = a[j], a[i]}fmt.Println(a) // [6 5 4 3 2 1]
}
另外,Go语言的for循环同样支持continue和break来控制循环,但是它提供了一个更加高级的break,可以选择中断哪一个循环,如下例子:
for j := 0; j < 5; j++ { for i := 0; i < 10; i++ { if i > 5 { break JLoop } fmt.Println(i) }
}
JLoop:
- 在Go中,函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句
package mainimport ("fmt"
)
import "errors"func Add(a int, b int) (ret int, err error) {if a < 0 || b < 0 { // 假设这个函数只支持两个非负数字的加法err = errors.New("Should be non-negative numbers!")return}return a + b, nil // 支持多重返回值
}func main() {res, err := _case.Add(1, 2)if err != nil {return}fmt.Println(res)
}
func Add(a, b int)(ret int, err error) { // ...
}
func Add(a, b int) int { // ...
}
2.2 函数调用
import "mymath"// 假设Add被放在一个叫mymath的包中// ...
c := mymath.Add(1, 2)
- 小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用
- 这个规则也适用于类型和变量的可见性
func myfunc(args ...int) { for _, arg := range args { fmt.Println(arg) }
}// 这段代码的意思是,函数myfunc()接受不定数量的参数,这些参数的类型全部是int,
// 所以它可以用如下方式调用:myfunc(2, 3, 4)
myfunc(1, 3, 7, 13)
形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数
它是一 个语法糖(syntactic sugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说,使用语法糖能够增加程序的可读性,从而减少程序出错的机会。
从内部实现机理来说说,类型...type本质上是一个数组切片,也就是[]type,这也是为什么上面的参数args可以用for循环来获得每个传入的参数
假如没有...type这样的语法糖,开发者将不得不这么写:
func myfunc2(args []int) { for _, arg := range args { fmt.Println(arg) }
}
myfunc2([]int{1, 3, 7, 13})
package mainimport ("fmt"
)func processFunc(args ...int) {for i, _ := range args {args[i] += 1}fmt.Println(args)
}func myfunc(args ...int) {// 按照原样传递processFunc(args...)// 传递片段,实际上任意的int slice都可以传进去processFunc(args[1:]...)
}func main() {myfunc(2, 3, 4)
}
(3)任意类型的不定参数
func Printf(format string, args ...interface{}) { // ...
}
用interface{}传递任意类型数据是Go语言的惯例用法。使用interface{}仍然是类型安全的,和C/C++不太一样
func MyPrintf(args ...interface{}) {for _, arg := range args {switch arg.(type) {case int:fmt.Println(arg, "is an int value.")case string:fmt.Println(arg, "is a string value.")case int64:fmt.Println(arg, "is an int64 value.")default:fmt.Println(arg, "is an unknown type.")}}
}func main() {var v1 int = 1var v2 int64 = 234var v3 string = "heheda"var v4 float32 = 1.234_case.MyPrintf(v1, v2, v3, v4)
}
(4)多返回值
与C、C++和Java等开发语言的一个极大不同在于,Go语言的函数或者成员的方法可以有多个返回值,这个特性能够让我们写出比其他语言更优雅,更简洁的代码。
比如File.Read()函数就可以同时返回读取的字节数和错误信息
如果读取文件成功,则返回值中的n为读取的字节数,err为nil,否则err为具体的出错信息:
func (file *File) Read(b []byte) (n int, err Error)
同样,从上面的方法原型可以看到,我们还可以给返回值命名,就像函数的输入参数一样。
- 返回值被命名之后,它们的值在函数开始的时候被自动初始化为空
- 在函数中执行不带任何参数的return语句时,会返回对应的返回值变量的值
Go语言并不需要强制命名返回值,但是命名后的返回值可以让代码更清晰,可读性更强,同时也可用于文档
如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,可以简单地用一个下划线"_"来跳过这个返回值,比如下面的代码表示调用者在读文件的时候不想关心Read()函数返回的错误码:
n, _ := f.Read(buf)
(5)匿名函数与闭包
① 匿名函数是指不需要定义函数名的一种函数实现方式,匿名函数由一个不带函数名的函数声明和函数体组成,如下所示:
func NMFunc() {f := func(x, y int) int {return x + y}fmt.Println(f(1, 2)) // 输出: 3// 创建一个通道replyChan := make(chan int)// 启动一个新的 Goroutine,执行匿名函数,并向通道发送数据go func(ch chan int) {ch <- 42}(replyChan) // 花括号后直接跟参数列表,表示调用// 从通道接收数据ack := <-replyChan// 打印接收到的数据fmt.Println(ack) // 输出: 42// 关闭通道close(replyChan)
}
②闭包:Go的匿名函数是一个闭包
基本概念:闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含 在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环境(作用域)。
闭包的价值:在于可以作为函数对象或者匿名函数
Go语言中的闭包:Go语言中的闭包同样会引用到函数外的变量,闭包的实现确保只要闭包还被使用,那么被闭包引用的变量会一直存在
closure.go
package main
import ( "fmt"
)
func main() { var j int = 5 a := func()(func()) { var i int = 10 return func() { fmt.Printf("i, j: %d, %d\n", i, j) } }() a() j *= 2 a()
}
上述例子的执行结果是:i, j: 10, 5i, j: 10, 10