【4.0】Go语言语法基础之函数

发布时间 2023-11-11 11:05:16作者: Chimengmeng

【一】函数基础

【1】语法

func关键字 函数名(形参1 形参1的类型,形参2 形参2的类型) (返回值类型) {
    函数体(函数体的内容和缩进无关,只要是在大括号内,都是函数体内容)
}
  • 在Python中需要先定义函数再使用
  • 在Go中不需要先定义再使用
func main() {

}

【2】无参数无返回值

package main

import "fmt"

func main() {
	// (2)在main函数中调用 index 函数
	index() // 这是index函数
}

// (1)简单的函数定义 :无参数 , 无返回值
// 定义函数后在main函数中调用
func index() {
	fmt.Println("这是index函数")
}

【3】有参数无返回值

  • 有参数无返回值,且参数类型不同
package main

import "fmt"

func main() {
	// (2)在main函数中调用 index 函数 需要传递两个参数,并且 参数要符合规定
	// 只有位置传参,没有所谓的关键字传参
	index("Dream", 18) // Dream 18
}

// (1)有参数无返回值的函数
// 定义函数后在main函数中调用
func index(name string, age int) {
	fmt.Println(name, age)
}
  • 有参数无返回值,且参数类型相同
package main

import "fmt"

func main() {
    // (2)在main函数中调用 index 函数 需要传递两个参数,并且 参数要符合规定
    // 只有位置传参,没有所谓的关键字传参
    index(19, 18) // 
}

// (1)有参数无返回值的函数
// 定义函数后在main函数中调用
// name 和 age 都是相同类型 可简写
func index(name, age int) {
    fmt.Println(name, age)
}
  • 有参数无返回值,且参数类型不同
package main

import "fmt"

func main() {
    // (2)在main函数中调用 index 函数 需要传递两个参数,并且 参数要符合规定
    // 只有位置传参,没有所谓的关键字传参
    index(19, 18, "dream","hope") 
}

// (1)有参数无返回值的函数
// 定义函数后在main函数中调用 name 和 age 都是数字类型 , gender 和 class 都是字符串类型
func index(name, age int,gender,class string) {
    fmt.Println(name, age,gender,class)
}

【4】2个参数和1个返回值

package main

import "fmt"

func main() {
	// (2)在main函数中调用 index 函数 需要传递两个参数,并且 参数要符合规定
	// 只有位置传参,没有所谓的关键字传参
	// 调用返回值需要先定义接收返回值的变量名
	res := index("Dream", 18)
	fmt.Println(res)
	// Dream 18
	// 18
}

// (1)两个参数和一个返回值
// 定义函数后在main函数中调用
func index(name string, age int) int {
	fmt.Println(name, age)

	// 返回值需要定义返回值类型,否则会报错
	// return age // Too many arguments to return
	return age // Too many arguments to return

}

【5】2个参数和2个返回值

package main

import "fmt"

func main() {
	// (2)在main函数中调用 index 函数 需要传递两个参数,并且 参数要符合规定
	// 只有位置传参,没有所谓的关键字传参
	// 调用返回值需要先定义接收返回值的变量名
	res, result := index("Dream", 18)
	fmt.Println(res, result)
	// Dream 18
	// Dream 18
}

// (1)两个参数和两个返回值 --- 多个返回值需要加 ()
// 定义函数后在main函数中调用
func index(name string, age int) (string, int) {
	fmt.Println(name, age)

	// 返回值需要定义返回值类型,否则会报错
	// return age // Too many arguments to return
	return name, age // Too many arguments to return
}

【6】_代替不需要接收的返回值

package main

import "fmt"

func main() {
	// (3)在main函数中调用 index 函数 需要传递两个参数,并且 参数要符合规定
	// 只有位置传参,没有所谓的关键字传参
	// 调用返回值需要先定义接收返回值的变量名 , 可以用 _ 代替不要的返回值
	//a, b, c := index(6, 18)
	//fmt.Println(a, b, c) // 6 18 108
	_, _, c := index(6, 18)
	fmt.Println(c) // 108

}

// (1)两个参数和两个返回值 --- 多个返回值需要加 ()
// 定义函数后在main函数中调用
func index(a, b int) (int, int, int) {

	// 返回值需要定义返回值类型,否则会报错
	return a, b, a * b
}

【7】命名返回值

package main

import "fmt"

func main() {
	// (4)在main函数中调用 index 函数 需要传递两个参数,并且 参数要符合规定
	// 只有位置传参,没有所谓的关键字传参
	// 调用返回值需要先定义接收返回值的变量名 , 可以用 _ 代替不要的返回值
	// 命名返回值的使用
	add, mul := index(6, 18)
	fmt.Println(add, mul) // 24 108
}

// (1)两个参数和两个返回值 --- 多个返回值需要加 ()
// 定义函数后在main函数中调用
func index(a, b int) (add int, mul int) {
	// [1] add / mul : 相当于定义了两个返回值的变量名,在函数内部不用定义就可以直接使用
	// 命名返回值
	// 返回值需要定义返回值类型,否则会报错
	//return a + b, a * b
	add = a + b
	mul = a * b

	// [2] return 可以直接省略掉 return add , mul
	return
}

【二】函数高级

【1】不定长参数

package main

import "fmt"

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数 : [1 2 3 4] 容器类型
	// Python 有可变长位置参数 和 可变长关键字参数 , 但是 go 只有 可变长位置参数
	foo(1, 2, 3, 4) // [1 2 3 4]
}

// 不定长参数
// 可以接收任意长度的数字类型的参数 : ...interface
func foo(a ...int) {
	fmt.Println(a)
}

【2】匿名函数

(1)基本的匿名函数

package main

import (
	"fmt"
)

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数 : [1 2 3 4] 容器类型
	// Python 有可变长位置参数 和 可变长关键字参数 , 但是 go 只有 可变长位置参数
	foo()
	// 这是 foo 函数内部
	//这是 foo 函数内部的匿名函数
}

// 匿名函数 --- 没有名字的函数 (需要定义在函数内部)

func foo(a ...int) {

	fmt.Println("这是 foo 函数内部")
	// 匿名函数 : 定义在函数内部的函数 , 必须是匿名函数
	func() {
		fmt.Println("这是 foo 函数内部的匿名函数")
	}() // 匿名函数要加 () 调用执行
}

(2)有参数的匿名函数

package main

import (
	"fmt"
)

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数 : [1 2 3 4] 容器类型
	// Python 有可变长位置参数 和 可变长关键字参数 , 但是 go 只有 可变长位置参数
	foo()
	// 这是 foo 函数内部
	//这是 foo 函数内部的匿名函数
}

// 匿名函数 --- 没有名字的函数 (需要定义在函数内部)

func foo(a ...int) {

	fmt.Println("这是 foo 函数内部")
	// 匿名函数 : 定义在函数内部的函数 , 必须是匿名函数
	// 有参数的匿名函数
	// 位置参数 a,b 均为 int 类型
	func(a, b int) {
		fmt.Println("这是 foo 函数内部的匿名函数")
	}(4, 5) // 匿名函数要加 () 调用执行,未知参数必须传入参数才不会报错
}

(3)有参数并且有返回值的匿名函数

package main

import (
	"fmt"
)

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数 : [1 2 3 4] 容器类型
	// Python 有可变长位置参数 和 可变长关键字参数 , 但是 go 只有 可变长位置参数
	foo()
	// 这是 foo 函数内部
	//这是 foo 函数内部的匿名函数
}

// 匿名函数 --- 没有名字的函数 (需要定义在函数内部)

func foo(a ...int) {

	fmt.Println("这是 foo 函数内部")
	// 匿名函数 : 定义在函数内部的函数 , 必须是匿名函数
	// 有参数的匿名函数
	// 位置参数 a,b 均为 int 类型
	// 有参数,有返回值的匿名函数
	res := func(a, b int) int {
		fmt.Println("这是 foo 函数内部的匿名函数")
		return a * b
	}(4, 5) // 匿名函数要加 () 调用执行,未知参数必须传入参数才不会报错

	fmt.Println("这是 匿名函数的返回值 :>>>> ", res)
}

【3】函数是一等公民

package main

import "fmt"

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数
	foo()
}

// 匿名函数也是一种类型 ---> 函数是一等公民 ---> 头等函数
func foo() {
	// 一门语言,只要函数是一等公民的意思是可以把函数当成变量来使用,传递

	// 函数是一等公民 ---> 可以把函数赋值给一个变量
	var f = func() {
		fmt.Println("我是 foo 函数内部的匿名函数")
	}
	// 调用函数
	f() // 我是 foo 函数内部的匿名函数
	// 查看函数的类型
	fmt.Printf("匿名函数的类型是 :>>>> %T", f) // 匿名函数的类型是 :>>>> func()

	// 既然函数是变量,就可以当参数传递,也可以当返回值返回
}
package main

import "fmt"

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数
	foo()
}

// 匿名函数也是一种类型 ---> 函数是一等公民 ---> 头等函数
func foo() {
	// 一门语言,只要函数是一等公民的意思是可以把函数当成变量来使用,传递

	// 函数是一等公民 ---> 可以把函数赋值给一个变量 -- 完整定义
	var f func() = func() {
		fmt.Println("我是 foo 函数内部的匿名函数")
	}
	// 调用函数
	f() // 我是 foo 函数内部的匿名函数
	// 查看函数的类型
	fmt.Printf("匿名函数的类型是 :>>>> %T", f) // 匿名函数的类型是 :>>>> func()

	// 既然函数是变量,就可以当参数传递,也可以当返回值返回
}

【4】闭包函数

(1)引入

package main

import "fmt"

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数
	foo()
}

// 闭包函数 : 必须符合两个 条件
// 1 :定义在函数内部
// 2 :对外部作用域有引用
func foo() func() {
	f := func() { // 这样写只满足了第一个条件 :定义在函数内部 , 所以不是闭包函数
		fmt.Println("我是 foo 函数内部的匿名函数")
	}
	return f
}

(2)闭包函数

  • Go 有闭包函数,但是没有装饰器的语法糖
package main

import "fmt"

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数
	f := foo()
	f()
	// 这是foo函数的结果调用  Dream
	// 我是 foo 函数内部的匿名函数
	// 这是foo函数 内部的匿名函数的结果调用  Dream
}

// 闭包函数 : 必须符合两个 条件
// 1 :定义在函数内部
// 2 :对外部作用域有引用
func foo() func() {
	name := "Dream"
	f := func() { // 【1】定义在函数内部,
		fmt.Println("我是 foo 函数内部的匿名函数")
		// 【2】并对外部作用域有引用
		fmt.Println("这是foo函数 内部的匿名函数的结果调用 ", name)
	}
	fmt.Println("这是foo函数的结果调用 ", name)
	return f
}

【5】函数的参数和返回值都是类型的一部分

(1)函数的参数是类型的一部分

package main

import "fmt"

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数
	f := foo()
	f(4, 5) // 我是 foo 函数内部的匿名函数  20
}

// 函数是一种类型,函数的参数和返回值都是类型的一部分
func foo() func(a, b int) {
	return func(a, b int) { 
		fmt.Println("我是 foo 函数内部的匿名函数 ", a*b)
	}
}

(2)函数的返回值是类型的一部分

package main

import "fmt"

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数
	f := foo()
	f() 
}

// 函数是一种类型,函数的参数和返回值都是类型的一部分
// func(a, b int) int  外层定义函数的参数类型及返回值类型
func foo() func(a, b int) int {
	// func(a, b int) int  内层定义 函数的参数类型及 内部函数的返回值类型
	return func(a, b int) int {
		fmt.Println("我是 foo 函数内部的匿名函数 ", a*b)
		return a * b
	}
}

(3)函数也可以作为参数

package main

import "fmt"

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数
	f := foo()
	f() 
}

// 函数是一种类型,函数的参数和返回值都是类型的一部分
// func(a int, b func()) int  外层定义函数的参数类型(a 为int,b 为一个函数)及返回值类型(返回了一个数字所以是 int)
func foo() func(a int, b func()) int {
	// func(a, b int) int  内层定义 函数的参数类型 (a 为int,b 为一个函数)及 内部函数的返回值类型(返回了一个数字所以是 int)
	return func(a int, b func()) int {
		fmt.Println("我是 foo 函数内部的匿名函数 ")
		return a
	}
}

【6】给类型重命名

package main

import "fmt"

// 补充 : 同一个包下,不能出现同名的变量和变量名 (py以py文件为最小单位,go以包为最小单位)
func main() {
	// 调用函数
	f := foo()
	f(4, 5) // 我是 foo 函数内部的匿名函数  20
}

// 给类型重命名
type Myfunc func(a int, b func()) int

// 函数是一种类型,函数的参数和返回值都是类型的一部分
// func(a int, b func()) int  外层定义函数的参数类型(a 为int,b 为一个函数)及返回值类型(返回了一个数字所以是 int)
func foo() Myfunc {
	// func(a, b int) int  内层定义 函数的参数类型 (a 为int,b 为一个函数)及 内部函数的返回值类型(返回了一个数字所以是 int)
	return func(a int, b func()) int {
		fmt.Println("我是 foo 函数内部的匿名函数 ")
		return a
	}
}