Go语言基础
介绍
基础
介绍
- 本文介绍Go语言中函数(函数定义、函数调用、函数形参、函数返回值、递归函数、匿名函数与闭包)等相关知识。
基础
函数
- 函数是对代码片段的逻辑封装的集合。函数的作用就是提供代码复用性。
- 函数之间属于并列关系,main 函数是程序执行的入口,且程序中只能有一个 main 函数。
- 函数名称必须遵守Go语言命名规范,首字母大写表示对其它包可见,首字母小写表示只对本包可见。
- 函数不支持函数重载,所以同一个包中的函数名必须不相同。
函数定义
- 函数包含函数名、形参列表、函数体和返回值列表,使用关键字 func 声明,基本语法如下:
func 函数名称 (形参列表) (返回值列表) {
函数体
return + 返回值列表
}
package main
import "fmt"
// 无形参、无返回值
func main() {
print(10, 20)
fmt.Printf("a + b = %v\n", sum(10, 20))
}
// 有形参、无返回值
func print(a, b int) {
fmt.Printf("a = %v, b = %v\n", a, b)
}
// 有形参、有返回值
func sum(a, b int) int {
return a + b
}
函数调用
- 函数之间属于并列关系,不允许在一个函数中定义另一个函数,可以在一个函数中调用另一个函数。
package main
import "fmt"
func main() { // main 函数中调用其它函数
empty() // 调用无参、无返回值函数
print(10, 20) // 调用有参、无返回值函数
fmt.Printf("a + b = %v\n", sum(10, 20)) // 调用有参、有返回值函数
sum(20, 30) // 忽略函数返回值
}
func empty() { // 无形参、无返回值
fmt.Println("empty")
}
func print(a, b int) { // 有形参、无返回值
fmt.Printf("a = %v, b = %v\n", a, b)
}
func sum(a, b int) int { // 有形参、有返回值
return a + b
}
输出结果
empty
a = 10, b = 20
a + b = 30
函数形参
- 形参列表可以是 0 到 多个形参。函数形参支持可变参数,将可变参当作切片处理。可变参数只能定义一个,且只能在形参列表末端
package main
import "fmt"
func main() {
test(10, 20, 30, 40, 50)
// 将切片解包传递给函数形参
test([]int{30, 40, 50}...)
s := []int{30, 40, 50}
test(s...)
test(s[:1]...)
}
func test(args ...int) { // 可变参函数,将可变参数初始化为切片处理
for i, v := range args {
fmt.Printf("args[%v] = %v\t", i, v)
}
fmt.Println()
}
输出结果
args[0] = 10 args[1] = 20 args[2] = 30 args[3] = 40 args[4] = 50
args[0] = 30 args[1] = 40 args[2] = 50
args[0] = 30 args[1] = 40 args[2] = 50
args[0] = 30
- 基本数据类型和数组是值传递,将实际参数拷贝一份到函数内部使用。若不想值传递,函数形参使用指针类型。
package main
import "fmt"
func main() {
var a int = 10
var b int = 20
fmt.Printf("swap before: a = %v, b = %v\n", a, b)
swap(a, b)
fmt.Printf("swap after: a = %v, b = %v\n", a, b)
fmt.Println("====================================")
fmt.Printf("swap before: a = %v, b = %v\n", a, b)
swap2(&a, &b)
fmt.Printf("swap after: a = %v, b = %v\n", a, b)
}
func swap(a int, b int) { // 基本数据类型,将实参拷贝一份
a, b = b, a
fmt.Printf("swap: a = %v, b = %v\n", a, b)
}
func swap2(a *int, b *int) { // 通过指针实现两值交换,指针指向实参内存位置
*a, *b = *b, *a
fmt.Printf("swap ptr: a = %v, b = %v\n", *a, *b)
}
输出结果
swap before: a = 10, b = 20
swap: a = 20, b = 10
swap after: a = 10, b = 20
====================================
swap before: a = 10, b = 20
swap ptr: a = 20, b = 10
swap after: a = 20, b = 10
- 形参列表中,对连续相同的类型可以对类型进行合并。
package main
import "fmt"
func main() {
test(1, 2)
test2(1, 2)
}
func test(a int, b int) { // 未合并形参类型
fmt.Printf("test: a = %v, b = %v\n", a, b)
}
func test2(a, b int) { // 连续相同的形参变量类型进行合并
fmt.Printf("test2: a = %v, b = %v\n", a, b)
}
输出结果
test: a = 1, b = 2
test2: a = 1, b = 2
函数返回值
- 返回值类型列表数量可以存在 0 到多个返回值。
package main
import "fmt"
func main() {
test()
fmt.Println("test1: ", test1())
a, b, c := test2()
fmt.Printf("test2: %v, %v, %v\n", a, b, c)
}
func test() { // 无返回值
fmt.Println("no return")
}
func test1() int { // 有一个返回值
return 10
}
func test2() (int, int, int) { // 有多个返回值
return 10, 20, 30
}
输出结果
no return
test1: 10
test2: 10, 20, 30
- 函数返回值可以命名,函数内部通过此变量名给返回值赋值。
package main
import "fmt"
func main() {
fmt.Println("test1: ", test1())
a, b, c := test2()
fmt.Printf("test2: %v, %v, %v\n", a, b, c)
d, e, f := test3()
fmt.Printf("test3: %v, %v, %v\n", d, e, f)
}
func test1() (r int) { // 有一个返回值
r = 10
return
}
func test2() (r1, r2, r3 int) { // 有多个返回值
r1 = 10
r2 = 20
r3 = 30
return
}
func test3() (r1 int, r2 int, r3 int) { // 有多个返回值
r1 = 10
r2 = 20
r3 = 30
return
}
输出结果
test1: 10
test2: 10, 20, 30
test3: 10, 20, 30
- 函数也可以作为一种数据类型,可存储在容器中,也可作为函数参数传入或作为函数返回值。
package main
import "fmt"
func main() {
// 函数类型变量
var f func() int = test1
fmt.Printf("a type: %T\n", f)
f()
// 函数类型作为形参
fmt.Println("test2: ", test2(test1))
// 函数类型作为返回值
fmt.Println("test3: ", test3()())
}
func test1() (r int) { // 有一个返回值
r = 10
return
}
func test2(f func() int) (r int) { // 有一个返回值
r = f()
return
}
func test3() (f func() int) { // 有一个返回值
f = test1
return
}
输出结果
a type: func() int
test2: 10
test3: 10
递归函数
- 递归函数是指在函数内部继续调用自己的函数。
- 编写递归函数时必须要有终止条件,否则会无限次调用,内存溢出。
package main
import "fmt"
func main() {
// 打印斐波那契数列
for i := 1; i < 10; i++ {
fmt.Print(fibonacci(i), ", ")
}
fmt.Println()
// 打印阶乘
for i := 1; i < 10; i++ {
fmt.Print(factorial(uint32(i)), ", ")
}
fmt.Println()
}
// 计算斐波那契数列,n < 3时值为1,n > 2时,数值为前两个位置数值之和
func fibonacci(n int) (ret int) {
if n < 3 {
ret = 1
return
}
ret = fibonacci(n-1) + fibonacci(n-2)
return
}
// 计算正整数的阶乘(所有小于等于该数的正整数的积),0 的阶乘为 1
// n!=1×2×3×…×n
func factorial(n uint32) (ret uint32) {
if n < 2 {
ret = 1
return
}
ret = n * factorial(n-1)
return
}
输出结果
1, 1, 2, 3, 5, 8, 13, 21, 34,
1, 2, 6, 24, 120, 720, 5040, 40320, 362880,
匿名函数与闭包
- 未指定函数名称的函数称之为匿名函数
- 可以在局部代码块中使用、也可以作为其它函数的形参类型
package main
import "fmt"
func main() {
// 定义匿名函数赋值给一个变量,通过变量调用匿名函数
print := func() {
fmt.Println("function 1")
}
print()
// 定义匿名函数并调用
func() {
fmt.Println("function 2")
}()
// 调用形参传递匿名函数
test(func() {
fmt.Println("function 3")
})
}
// 匿名函数作为函数形参
func test(print func()) {
print()
}
输出结果
function 1
function 2
function 3
- 匿名函数又可以称为闭包,在函数内部可以定义匿名函数,此匿名函数可以引用外部函数的变量。
- 只要使用外部函数变量,其匿名函数生命周期一直存在,不会被自动销毁。
package main
import "fmt"
func main() {
test()
}
// 定义闭包函数,以引用方式访问外部变量
func test() {
var a int = 10
func() {
fmt.Printf("func() a = %v\n", a)
a = 20
}()
fmt.Printf("test() a = %v\n", a)
fmt.Println("test2(): ", test2("Hello")(" World!"))
}
// 函数内部定义闭包函数作为返回值
func test2(a string) func(string) string { // 返回值为匿名函数
return func(b string) string {
return a + b
}
}
输出结果
func() a = 10
test() a = 20
test2(): Hello World!