> 文章列表 > 初识Go语言10-函数【函数的基本形式、匿名函数】

初识Go语言10-函数【函数的基本形式、匿名函数】

初识Go语言10-函数【函数的基本形式、匿名函数】

文章目录

  • 函数
    • 参数
      • 函数参数
    • 函数的基本形式
    • 匿名函数

函数

参数

函数参数

函数定义时指出,函数定义时有参数,该变量可称为函数的形参。形参就像定义在函数体内的局部变量。

但当调用函数,传递过来的变量就是函数的实参,函数可以通过两种方式来传递参数:

值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

func swap(x, y int) int {... ...}

引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

package mainimport ("fmt"
)/* 定义相互交换值的函数 */
func swap(x, y *int) {var temp inttemp = *x /* 保存 x 的值 */*x = *y   /* 将 y 值赋给 x */*y = temp /* 将 temp 值赋给 y*/}func main() {var a, b int = 1, 2/*调用 swap() 函数&a 指向 a 指针,a 变量的地址&b 指向 b 指针,b 变量的地址*/swap(&a, &b)fmt.Println(a, b)
}

输出结果:

    2 1

在默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。

注意1:无论是值传递,还是引用传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。引用传递是地址的拷贝,一般来说,地址拷贝更为高效。而值拷贝取决于拷贝的对象大小,对象越大,则性能越低。

注意2:map、slice、chan、指针、interface默认以引用的方式传递。

不定参数传值
就是函数的参数不是固定的,后面的类型是固定的。(可变参数)

Golang 可变参数本质上就是 slice。只能有一个,且必须是最后一个。

在参数赋值时可以不用用一个一个的赋值,可以直接传递一个数组或者切片,特别注意的是在参数后加上“…”即可。

  func myfunc(args ...int) {    //0个或多个参数}func add(a int, args…int) int {    //1个或多个参数}func add(a int, b int, args…int) int {    //2个或多个参数}

注意:其中args是一个slice,我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数.

任意类型的不定参数:
就是函数的参数和每个参数的类型都不是固定的。

用interface{}传递任意类型数据是Go语言的惯例用法,而且interface{}是类型安全的。

func myfunc(args ...interface{}) {}

代码:

package mainimport ("fmt"
)func test(s string, n ...int) string {var x intfor _, i := range n {x += i}return fmt.Sprintf(s, x)
}func main() {println(test("sum: %d", 1, 2, 3))
}

输出结果:

    sum: 6

使用 slice 对象做变参时,必须展开。(slice...)

package mainimport ("fmt"
)func test(s string, n ...int) string {var x intfor _, i := range n {x += i}return fmt.Sprintf(s, x)
}func main() {s := []int{1, 2, 3}res := test("sum: %d", s...)    // slice... 展开sliceprintln(res)
}

函数的基本形式

//函数定义。a,b是形参
func argf(a int, b int) { a = a + b 
}
var x, y int = 3, 6
argf(x, y) //函数调用。x,y是实参
  • 形参是函数内部的局部变量,实参的值会拷贝给形参。
  • 函数定义时的第一个的大括号不能另起一行。
  • 形参可以有0个或多个。
  • 参数类型相同时可以只写一次,比如argf(a,b int)。
  • 在函数内部修改形参的值,实参的值不受影响。
  • 如果想通过函数修改实参,就需要指针类型。
func argf(a, b *int) { *a = *a + *b*b = 888
}
var x, y int = 3, 6
argf(&x, &y)

  slice、map、channel都是引用类型,它们作为函数参数时其实跟普通struct没什么区别,都是对struct内部的各个字段做一次拷贝传到函数内部。

package mainimport "fmt"func slice_arg_1(arr []int) { //slice作为参数,实际上是把slice的arrayPointer、len、cap拷贝了一份传进来arr[0] = 1           //修改底层数据里的首元素arr = append(arr, 1) //arr的len和cap发生了变化,不会影响实参
}func main() {arr := []int{8}slice_arg_1(arr)fmt.Println(arr[0])   //1fmt.Println(len(arr)) //1
}

关于函数返回值

  • 可以返回0个或多个参数。
  • 可以在func行直接声明要返回的变量。
  • return后面的语句不会执行。
  • 无返回参数时return可以不写。
func returnf(a, b int) (c int) { //返回变量c已经声明好了a = a + bc = a //直接使用creturn //由于函数要求有返回值,即使给c赋过值了,也需要显式写return
}

  不定长参数实际上是slice类型。

func variable_ength_arg(a int, other ...int) int { sum := afor _, ele := range other {//不定长参数实际上是slice类型sum += ele}fmt.Printf("len %d cap %d\\n", len(other), cap(other))return sum
}
variable_ength_arg(1)
variable_ength_arg(1,2,3,4)

  append函数接收的就是不定长参数。

arr = append(arr, 1, 2, 3)
arr = append(arr, 7)
arr = append(arr)
slice := append([]byte("hello "), "world"...) //...自动把"world"转成byte切片,等价于[]byte("world")...
slice2 := append([]rune("hello "), []rune("world")...) //需要显式把"world"转成rune切片

  在很多场景下string都隐式的转换成了byte切片,而非rune切片,比如"a中"[1]是228而非"中"。
递归函数

func Fibonacci(n int) int {if n == 0 || n == 1 {return n //凡是递归,一定要有终止条件,否则会进入无限循环}return Fibonacci(n-1) + Fibonacci(n-2) //递归调用函数自身
}

匿名函数

  函数也是一种数据类型。

func function_arg1(f func(a, b int) int, b int) int { //f参数是一种函数类型a := 2 * breturn f(a, b)
}type foo func(a, b int) int //foo是一种函数类型
func function_arg2(f foo, b int) int { //参数类型看上去简洁多了a := 2 * breturn f(a, b)
}type User struct {Name stringbye foo //bye的类型是foo,而foo代表一种函数类型hello func(name string) string //使用匿名函数来声明struct字段的类型
}ch := make(chan func(string) string, 10)
ch <- func(name string) string {  //使用匿名函数return "hello " + name
}

在Go里面,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。

匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。

package mainimport ("fmt""math"
)func main() {getSqrt := func(a float64) float64 {return math.Sqrt(a)}fmt.Println(getSqrt(4))
}

输出结果:

    2

上面先定义了一个名为getSqrt 的变量,初始化该变量时和之前的变量初始化有些不同,使用了func,func是定义函数的,可是这个函数和上面说的函数最大不同就是没有函数名,也就是匿名函数。这里将一个函数当做一个变量一样的操作。

Golang匿名函数可赋值给变量,做为结构字段,或者在 channel 里传送。

package mainfunc main() {// --- function variable ---fn := func() { println("Hello, World!") }fn()// --- function collection ---fns := [](func(x int) int){func(x int) int { return x + 1 },func(x int) int { return x + 2 },}println(fns[0](100))// --- function as field ---d := struct {fn func() string}{fn: func() string { return "Hello, World!" },}println(d.fn())// --- channel of function ---fc := make(chan func() string, 2)fc <- func() string { return "Hello, World!" }println((<-fc)())
}

输出结果:

    Hello, World!101Hello, World!Hello, World!