Go 语言编程 — 函数
目录
定义一个函数
函数声明需要指定:
- 函数的名称
- 形参列表
- 返回值列表
函数名和形参列表一起构成函数签名。格式:
func function_name([parameter list]) [return_types] { 函数体
}
- 1
- 2
- 3
示例:
func max(num1, num2 int) int { var result int if (num1 > num2) { result = num1 } else { result = num2 } return result
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
形参列表
值传递
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。默认情况下,Golang 使用的是值传递,即在调用过程中不会影响到实际参数。
示例:
package main
import "fmt"
func swap(x, y int) int { var temp int temp = x x = y y = temp return temp;
}
func main() { var a int = 100 var b int = 200 fmt.Printf("交换前 a 的值为 : %d\n", a) fmt.Printf("交换前 b 的值为 : %d\n", b) swap(a, b) fmt.Printf("交换后 a 的值 : %d\n", a) fmt.Printf("交换后 b 的值 : %d\n", b)
}
- 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
结果:可见,值传递的方式不会影响到实参变量的原数值
交换前 a 的值为 : 100
交换前 b 的值为 : 200
交换后 a 的值 : 100
交换后 b 的值 : 200
- 1
- 2
- 3
- 4
引用传递
引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。类似于 C 语言函数定义中的指针类型形参。
引用传递方式,将指针参数传递到函数内,以下是交换函数 swap() 使用了引用传递:
package main
import "fmt"
func swap(x, y *int) { *x, *y = *y, *x
}
func main() { var a int = 100 var b int= 200 fmt.Printf("交换前,a 的值 : %d\n", a) fmt.Printf("交换前,b 的值 : %d\n", b) swap(&a, &b) fmt.Printf("交换后,a 的值 : %d\n", a) fmt.Printf("交换后,b 的值 : %d\n", b)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
结果:
交换前,a 的值 : 100
交换前,b 的值 : 200
交换后,a 的值 : 200
交换后,b 的值 : 100
- 1
- 2
- 3
- 4
返回值
Go 函数可以返回多个值,例如:
package main
import "fmt"
func swap(x, y string) (string, string) { return y, x
}
func main() { a, b := swap("hello", "world") fmt.Println(a, b)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
结果:
world hello
- 1
初始化函数
Golang 程序的初始化流程:
- 初始化导入的包,包的初始化顺序由 runtime 需要解析包依赖关系决定,没有依赖的包最先被初始化。
- 初始化包作用域的变量,该作用域的变量初始化也并非按照 “从上到下、从左到右” 的顺序,同样由 runtime 解析变量依赖关系,没有依赖的变量最先初始化。
- 执行包的 init 函数;
Golang 提供了一个特殊的初始化函数 init(),会先于 main() 函数执行,通常用于实现包级别的一些初始化操作。例如:在 ORM database 包中的 init() 通常用于实现 AutoMigrate(数据库初始化)。
init() 的主要特点:
- init 函数先于 main 函数执行,且不能被其他函数调用;
- init 函数没有输入参数,也没有返回值;
- 每个包可以有多个 init 函数;
- 包的每个源文件也可以有多个 init 函数,这点比较特殊;
- Golang 没有明确定义同一个包中多个 init 函数的执行顺序,编程时要注意程序不要依赖 init 函数的执行顺序。
- 不同包之间的 init 函数按照包导入的依赖关系决定执行顺序。
构造函数
用于构造一个实例对象的函数,build-in 的构造函数有 new() 用于构造自建数据类型的实例。如果是自定义派生数据类型的构造函数通常是开发者自己实现的。
析构函数
Golang 原生没有析构函数,需要开发者额外的人工处理资源清理的工作,通常会使用 defer 语句来完成这些工作。
回调函数
回调函数,一个函数作为另外一个函数的实参,即:向函数传递一个函数的引用。显然,这是一个引用传递的场景。这与 C 语言中的函数指针(指向函数的指针)类似。
所以,在传递一个函数之前,首先需要创建一个函数变量,函数变量的名称就是函数自身的一个引用。
package main
import ( "fmt" "math"
)
func get_square_root(x float64) float64 { return math.Sqrt(x)
}
func main() { fmt.Println(get_square_root(9))
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
结果:
3
- 1
或者,可以直接在一个函数体中定义一个新的函数:
package main
import ( "fmt" "math"
)
func main() { /* 定义函数变量。 */ get_square_root := func(x float64) float64 { return math.Sqrt(x) } fmt.Println(get_square_root(9))
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
实际上,当我们定义一个接受函数实参的函数时,首先需要声明函数形参的类型:
package main
import "fmt"
func test_call_back(x int, f func(int) int) { f(x)
}
func call_back(x int) int { fmt.Printf("我是回调,x:%d\n", x) return x
}
func main() { test_call_back(1, call_back)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
更好的一种写法:
package main
import "fmt"
type call_back_func_t func(int) int
func test_call_back(x int, f call_back_func_t) { f(x)
}
func call_back(x int) int { fmt.Printf("我是回调,x:%d\n", x) return x
}
func main() { test_call_back(1, call_back)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
再看一种写法:
package main
import "fmt"
type call_back_func_t func(int) int
func test_call_back(x int, f call_back_func_t) { f(x)
}
func main() { test_call_back(1, func(x int) int { fmt.Printf("我是回调,x:%d\n", x) return x })
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
闭包(Closure)函数
闭包(Closure):如果内层函数引用了外层函数的局部变量,并且在外层函数中 return 内层函数,这种关系就称之为闭包。
可见,闭包的特点是发生在函数嵌套的基础上实现。外层函数返回的内层函数还引用了外层函数的局部变量,所以要想正确的使用闭包,那么就要确保这个被内层函数引用的局部变量是不变的。Python 中使用闭包函数来实现了装饰器机制。
Golang 支持匿名函数,可用于实现闭包。匿名函数是一个表达式吗,其优越性在于可以直接使用函数内的变量,而不必声明函数名。以下示例中,定义了函数 getSequence() 并返回了另外一个函数。getSequence() 函数的目的是在闭包中递增 i 变量。
package main
import "fmt"
/**
* get_sequence 函数作为外层函数,返回匿名函数 func() int,
* 匿名函数 func() int 作为内层函数,并且直接引用了外层函数的局部变量 i,
* 如此的,就形成了一个闭包。
*/
func get_sequence() func() int { i := 0 return func() int { i += 1 return i }
}
func main() { /* 定义一个闭包函数变量,当前 i 变量为 0。 */ next_num := get_sequence() /* 每调用一次闭包函数,变量 i += 1 并返回。 */ fmt.Println(next_num()) fmt.Println(next_num()) fmt.Println(next_num()) /* 定义另一个闭包函数变量。 */ next_num1 := get_sequence() fmt.Println(next_num1()) fmt.Println(next_num1()) fmt.Println(next_num1())
}
- 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
结果:
1
2
3
1
2
3
- 1
- 2
- 3
- 4
- 5
- 6
方法函数
在常规的面向对象编程语言(OOP)中,比如 Python,函数(Function)和方法(Method)是属于两个不同的术语:
- 在类中定义的称为方法,称为类对象的成员方法。
- 不在类中定义的称为独立函数。
虽然 Golang 不是一种 OOP 类型编程语言,没有类的概念,但它也同样支持为数据类型定义相应的 Method。所谓的 Method 其实就是函数,只不过与普通函数相比,这类函数是作用在某个数据类型上的。
所以,在函数签名中,会有个 Receiver(接收器)来表明当前定义的函数会作用在该 Receiver 上。一个 Method 就是一个包含了 Receiver 的函数,接受者可以是:“命名(type)类型” 或者 “结构体类型” 的一个值或一个指针。例如下述的 (variable_name variable_data_type)
。
func (variable_name variable_data_type) function_name() [return_type]{ /* 函数体*/
}
- 1
- 2
- 3
实际上,Golang 支持对除 Interface 类型外的任何数据类型定义其 Method,只不过实际编程中,Method 多定义在结构体上而已。
从这点上,我们可以简易的将结构体类型变量理解为 Python 中对象的概念,而对象可以调用属于他自身的方法。这是 Golang 对 OOP 编程机制的一种补充。
示例:
package main
import "fmt"
/* 自定义结构体类型 */
type circle_t struct { radius float64
}
// 定义属于 circle 类型对象的 Method
func (c circle_t) get_area() float64 { // c.radius 即为 circle 类型对象中的属性 return 3.14 * c.radius * c.radius
}
func main() { // 定义一个结构体类型对象 c1 var c1 circle_t c1.radius = 10.00 fmt.Println("圆的面积 = ", c1.get_area())
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
从上述例子可见,方法 get_area 就像是变量 c1 的成员方法一般。
递归函数
递归,就是在运行的过程中调用自己。Golang 支持递归调用,但我们在使用递归时,需要合理设置退出条件,否则递归将陷入无限循环中。
格式:
func recursion() { recursion() /* 函数调用自身 */
}
func main() { recursion()
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。
- 阶乘:
package main
import "fmt"
func Factorial(n uint64) (result uint64) { if (n > 0) { result = n * Factorial(n-1) return result } return 1
}
func main() { var i int = 15 fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 斐波那契数列:
package main
import "fmt"
func fibonacci(n int) int { if n < 2 { return n } return fibonacci(n-2) + fibonacci(n-1)
}
func main() { var i int for i = 0; i < 10; i++ { fmt.Printf("%d\t", fibonacci(i)) }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
文章来源: is-cloud.blog.csdn.net,作者:范桂飓,版权归原作者所有,如需转载,请联系作者。
原文链接:is-cloud.blog.csdn.net/article/details/107140665
- 点赞
- 收藏
- 关注作者
评论(0)