函数

函数定义格式 #

func 函数名(参数列表)(返回值列表){
    函数体
}
1.无参且无返回值
    func 方法名(){

    }
2.有参且无返回值
    func 方法名(参数列表且多个逗号分隔){

    }
3.有参且有返回值
    func 方法名(参数列表且多个逗号分隔) (返回值类型列表且多个逗号分隔){

    }

    func 方法名(参数名1,参数名2 参数类型, 参数名3 参数类型) 返回值类型{

    }

4.可变参数
    func 方法名1(v ...可变参数类型) (返回参数列表){
        //可变参数,说明参数个数不固定,在可变参数类型前加...(即3个点)
        //把可变参数原样传递给其它方法时,只要在可变参数名后加...
        方法名2(v...)
    }
    func 方法名2(固定参数列衰, v ...可变参数类型) (返回参数列表){
        //可变参数,说明参数个数不固定,在可变参数类型前加...(即3个点)
        //把可变参数原样传递给其它方法时,只要在可变参数名后加...
        方法名2(v...)
    }
5.匿名函数:
  func(参数列表)(返回值){
      函数体
  }

备注:方法名又叫函数名
    方法名可由字母、数字、下划线组成,且不能以数字开头
    在同一包内方法名不能重复(即不能重名,方法名不能相同,因为go方法不支持重载。重写)
    参数:参数由参数变量名和参数变量的类型组成,多个参数之间使用,分隔。
    返回值:返回值由返回值变量名和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用逗号,分隔。
    函数体:实现指定功能的代码块。
    不支持默认值参数(即参数没有默认值)
    不支持函数重载
    不支持有名函数嵌套定义,但支持匿名函数嵌套在内

函数定义示例 #

//多个参数和多返回值示例,相邻参数/变量类型相同则类型可省略
func test1(a,b int) (int, int){//等价 func test1(a int,b int) (int, int){
    return a+b, a*b
}
a1,b1 := test1(5, 6)
//返回值带有变量的示例
func test2(a,b int) (q, r int){
    q = a+b
    r = a*b
    return        // 自动对号入座返回相应变量
}
q, r := test2(1,2)

//返回错误对象
func test3(a,b int)(int, error)  {
    if a+b>100{
        return a+b, fmt.Errorf("%s","error!")
    }else{
        return a+b, nil
    }
}
sum,err:=test3(2,3)

函数变量 #

函数变量: 就是把方法赋值给变量
var 变量名 func()  //声明变量存无参无返回值函数
变量名 := 方法名   //给函数类型变量赋值
var 变量名 func() 返回值类型
var 变量名 func() (返回值类型列表,多个逗号分隔) //声明变量存无参有返回值函数
var 变量名 func(参数类型列表) 返回值类型
var 变量名 func(参数类型列表) (返回值类型列表) //声明变量存有参有返回值函数
调用变量函数: 变量名()、变量名(参数列表)
类型相同的变量才可以直接赋值
判断函数类型相同:形参列表相同和返回值列表也要相同(即列表元素的次序、个数和类型均相同)
函数类型变量的默认值为nil
    var doFun  func(int, int) (int, int)
    fmt.Printf("%v \n", doFun) //<nil>
示例1:
package main
import "fmt"

func main()  {
	_getName01 := getName01  //变量函数
	var _getName02 func(string) string
		_getName02 = getName01
	fmt.Println(_getName01("tom")) //hi,tom
	fmt.Println(_getName02("jelly")) //hi,jelly
}
func getName01(name string) string {
	return "hi," + name
}

示例2:
package main
import "fmt"
func main() {
	var a,b int = 8, 3
	var doFun  func(int, int) (int, int) //定义函数类型变量
	doFun = valDemo  //给函数类型变量赋值
	s1,s2:=doFun(a, b) //调用函数
	fmt.Println(s1, s2) //11 5
	fmt.Printf("%T \n", valDemo) //func(int, int) (int, int)
}
func valDemo(a,b int) (int, int)  {
	sum := a+b
	sub := a-b
	return sum,sub
}

函数类型与变量 #

1. 定义函数类型,
    格式:type 类型名 func(参数) 返回值类型列表
    示例:type abcFunc func(int, int) int
2.
    func add(x, y int) int {//相加
    	return x + y
    }
    func sub(x, y int) int {//相减
    	return x - y
    }
    var c abcFunc  //定义变量类型
    c = add   //给变量赋值函数名
    调用:
    fmt.Printf("type of c:%T\n", c) //type of c:main.abcFunc
    fmt.Println(c(1, 2)) //3

匿名函数 #

匿名函数: 没有方法名的函数、又叫闭包
匿名函数用途:可以赋值给变量、也可以作为方法参数传入、也可以作为方法返回值返回
匿名函数定义:
    func(参数列表)(返回参数列表){
        函数体
    }
闭包自执行(匿名函数自执行):
    func(参数列表)(返回参数列表){
        函数体
    }(参数列表)

匿名函数赋值给变量:
    变量名 := func(参数列表)(返回参数列表){
               函数体
            }
            
匿名函数赋值给变量示例:
    myF := func() string{// 将匿名函数保存到变量
        return "my name is jelly"
    }
//调用
fmt.Println(myF()) //my name is jelly
匿名函数用途:多用于实现回调函数和闭包

匿名函数作为参数传递示例 #

示例1-接收匿名函数:
package main
import "fmt"
func main()  {
	myNum := []int{1, 3,4,8,10}
	//匿名函数作为参数传入
	myPrint(myNum, func(i int) {
		fmt.Println("总值为" ,i) //总值为 26
	})
}
//参数接收方法,参数的定义:变量名 方法类型
func myPrint(num []int, fn func(int)) int {
	var total = 0
	for _, i := range num { //求和
		total += i;
	}
	//回调
	fn(total)
	return total
}
示例2-返回匿名函数:
func adder2(x int) func(int) int {
	return func(y int) int {
		x += y
		return x
	}
}
func main() {
	var f = adder2(10)
	fmt.Println(f(10)) //20
	fmt.Println(f(20)) //40
	fmt.Println(f(30)) //70

	f1 := adder2(20)
	fmt.Println(f1(40)) //60
	fmt.Println(f1(50)) //110
}
示例3-参数接收函数:
func add(x, y int) int {
	return x + y
}
func calc(x, y int, op func(int, int) int) int {
	return op(x, y)
}
func main() {
	ret2 := calc(10, 20, add)
	fmt.Println(ret2) //30
}
示例4-自动加后缀:
func makeSuffixFunc(suffix string) func(string) string {
	return func(name string) string {
		if !strings.HasSuffix(name, suffix) {
			return name + suffix
		}
		return name
	}
}
func main() {
	jpgFunc := makeSuffixFunc(".jpg")
	txtFunc := makeSuffixFunc(".txt")
	fmt.Println(jpgFunc("test")) //test.jpg
	fmt.Println(txtFunc("test")) //test.txt
}

方法返回匿名函数示例 #

package main
import "fmt"

func main()  {
	myF01 := myFunc01(2)
	fmt.Println(myF01()) //3
	fmt.Println(myF01()) //4

	myF02 := myFunc01(100)
	fmt.Println(myF02()) //101
	fmt.Println(myF02()) //102
}
//函数返回闭包(即匿名函数),这个闭包的用途:计数
func myFunc01(num int) func() int {
	//返回闭包
	return func() int {
		num++  //每执行一次均对参数num变量+1
		return num
	}
}

函数作为返回值 #

func add(x, y int) int {
	return x + y
}
func sub(x, y int) int {
	return x - y
}
func do(s string) (func(int, int) int, error) {
	switch s {
	case "+":
		return add, nil
	case "-":
		return sub, nil
	default:
		err := errors.New("无法识别的操作符")
		return nil, err
	}
}
func main() {
	ret2,err := do("+")
	if(err== nil) {
		fmt.Println(ret2(1, 8)) //9
	} else {
		fmt.Println("不支持的方法," + err.Error())
	}

}

可变参数示例 #

package main
import "fmt"

func main()  {
	val01 := myFunc01(2,3,4)
	fmt.Println(val01) //9
	var a []int = []int{1,4,7} //定义切片
    fmt.Println(myFunc02(a...)) //12
}
//可变参数
func myFunc01(num ...int) int {
	total := myFunc02(num...)  //可变参数值传递给其它方法
	return total
}
func myFunc02(n ...int) int {
	t :=0
	for _,v:= range n{
		t +=v
	}
	return t
}

函数中访问全局变量函数示例 #

package main
import "fmt"
//定义全局变量num
var num int64 = 10
func printNum01() {
    //证明:函数中可以访问全局变量num
	fmt.Printf("num=%d\n", num)
}
func main() {
	printNum01() //打印:num=10
}
备注:如果局部变量和全局变量重名,优先访问局部变量
package main
import "fmt"
//定义全局变量num
var num int64 = 10
func printNum02() {
	num := 100  //局部变量与全局变量重名
	fmt.Printf("num=%d\n", num) // 函数中优先使用局部变量
}
func main() {
	printNum02() // num=100
}

按值传递与引用传递 #

引用传递: 切片(slice)、map(字典)、接口(interface)、通道(信道 channel)、指针类型
按值传递: 数组、int系列(数值型)、string、bool、float系列(浮点值)、结构体

示例1-切片引用传递:
package main
import (
	"fmt"
)
func main()  {
	var xyz map[string]string = map[string]string{"abc":"123", "x":"x1"}
	hi(xyz)
	fmt.Println(xyz) //map[abc:hello x:x1 appendc:c12]
	fmt.Println(xyz["appendc"]) //c12
}
func hi(a map[string]string)  {
	a["abc"] = "hello"
	a["appendc"] = "c12"
}

示例2-切片引用传递:
package main
import (
	"fmt"
)
func main()  {
	a := []int{2, 3}
	b := a   //切片变量赋值为引用赋值
	fmt.Println(a,b) //[2 3] [2 3]
	b[1] = 77
	fmt.Println(a,b) //[2 77] [2 77]
}

示例3-切片引用传递:
package main
import (
	"fmt"
)
func main()  {
	a := []int{2, 3}
	edit(a)   //切片引用传递
	fmt.Println(a) //[2 99]
}
func edit(a []int)  {//参数接收切片
	a[1] = 99
}
示例4-数组按值传递:
    a := [2]int{2, 3}
	b := a  //数组按值传递
	fmt.Println(a,b) //[2 3] [2 3]
	b[1] = 8
	fmt.Println(a,b)//[2 3] [2 8]

示例5-指针引用传递:
package main
import (
	"fmt"
)
func main()  {
	a := 8
	say(&a)   //指针引用传递
	fmt.Println(a) //99
}
func say(a *int)  {
	*a = 99
}

main函数和init函数 #

Go有两个保留的函数分别是main()和init()方法
main(): 入口函数
init():包初始化函数
相同点:
    1. Go程序会自动调用init()和main()方法,即不需要在任何地方主动调用这两个函数
    2. 均无参无返回值,自动调用,即无法主动调用初始化函数和main()函数

差异性:
    1. main()方法在package main包中定义与使用,作为程序的入口函数
    2. 每个包中均可定义一个或多个init()方法,但强烈建议开发者在一个package中每个文件只写一个init函数

main函数引入包初始化流程图如下:
main函数引入包初始化流程图
main函数引入包初始化流程图

实参与形参的传递示例 #

按值传递、引用传递
package main

import "fmt"

func main() {
	var a int = 4
	pointerDemo(&a)
	fmt.Println(a) //9

	valDemo(a)
	fmt.Println(a) //9

	fmt.Printf("%T \n", pointerDemo) //func(*int)
	fmt.Printf("%T \n", valDemo) //func(int)
}
//指针参数引用传递
func pointerDemo(a *int)  {
	*a = *a + 5
}
//普通变量按值传递
func valDemo(a int)  {
	a = a+1
}

go内置全局函数 #

func len(v Type) int 用来获取长度,比如字符串string、数组array、切片slice、map、信道channel等类型值的长度
func cap(v Type) int 获取容量大小

func print(args ...Type) 打印1个或多个变量值
func println(args ...Type) 打印1个或多个变量值,并换行

func complex(r, i FloatType) ComplexType
func real(c ComplexType) FloatType 取复数的实部数
func imag(c ComplexType) FloatType 取复数的虚部数

func panic(v interface{}) 抛出错误,主动发起恐慌
func recover() interface{} 捕获恐慌,解决宕机

func new(Type) *Type 返回指针
func make(t Type, size ...IntegerType) Type 初始化切片/map/chan变量,返回切片、map,通道类型

func delete(m map[Type]Type1, key Type) 删除map的key
func append(slice []Type, elems ...Type) []Type 向切片追加元素
func copy(dst, src []Type) int 复制切片
func close(c chan<- Type) 关闭通道
Build by Loppo 0.6.15