目录
定义一个函数
函数声明需要指定:
- 函数的名称
- 形参列表
- 返回值列表
函数名和形参列表一起构成函数签名。格式:
func function_name([parameter list]) [return_types] {
函数体
}
示例:
func max(num1, num2 int) int {
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
形参列表
值传递
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。默认情况下,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)
}
结果:可见,值传递的方式不会影响到实参变量的原数值
交换前 a 的值为 : 100
交换前 b 的值为 : 200
交换后 a 的值 : 100
交换后 b 的值 : 200
引用传递
引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。类似于 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)
}
结果:
交换前,a 的值 : 100
交换前,b 的值 : 200
交换后,a 的值 : 200
交换后,b 的值 : 100
返回值
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)
}
结果:
world hello
初始化函数
构造函数
析构函数
回调函数
回调函数,一个函数作为另外一个函数的实参,即:向函数传递一个函数的引用。显然,这是一个引用传递的场景。这与 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))
}
结果:
3
或者,可以直接在一个函数体中定义一个新的函数:
package main
import (
"fmt"
"math"
)
func main() {
/* 定义函数变量。 */
get_square_root := func(x float64) float64 {
return math.Sqrt(x)
}
fmt.Println(get_square_root(9))
}
实际上,当我们定义一个接受函数实参的函数时,首先需要声明函数形参的类型:
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)
}
更好的一种写法:
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)
}
再看一种写法:
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
})
}
反射函数
闭包(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
1
2
3
方法函数
在常规的面向对象编程语言(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]{
/* 函数体*/
}
实际上,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())
}
从上述例子可见,方法 get_area 就像是变量 c1 的成员方法一般。
递归函数
递归,就是在运行的过程中调用自己。Golang 支持递归调用,但我们在使用递归时,需要合理设置退出条件,否则递归将陷入无限循环中。
格式:
func recursion() {
recursion() /* 函数调用自身 */
}
func main() {
recursion()
}
递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。
- 阶乘:
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)))
}
- 斐波那契数列:
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))
}
}