• Golang 函数function


    函数function

    • Go函数不支持嵌套、重载和默认参数
    • 但支持以下特性:

    1. 无需声明原型
    2. 不定长度变参
    3. 多返回值
    4. 命名返回值参数
    5. 匿名函数
    6. 闭包
    • 定义函数使用关键字func,且左大括号不能另起一行
    • 函数也可以作为一种类型使用

    返回值及参数说明

    func A(a int, b string) (int, string, int)  { //第一个小括号当中是你的参数列表,第二个小括号是你的返回值列表
    	
    }
    
    func A(a, b, c int) (int, string, int)  {
    	//如果abc都是int型的话,可以按照这种方法进行简写,同样的方法也适用于返回值当中
    
    }
    
    func A() (a, b, c int)  { //1:如果这样写的话就必须要命名返回值
    	//命名返回值和不命名返回值得区别
    }
    
    func A() (int, int, int)  { //
    	//命名返回值和不命名返回值得区别
    	a, b, c := 1,2,3
    	return a,b,c
    	//如果此时没有命名返回值的时候,那么在返回值得时候就必须写上return a,b,c
    	//当然为了代码的可读性,这里我们规定必须return 的时候加上返回值名
    }
    

    不定长变参

    package main
    
    import "fmt"
    
    func main()  {
    	A(1,2,3,4,5,6,7)
    
    
    }
    
    func A(a ...int) {
    	// 这里采用的是不定长变参,不定长变参必须是参数的最后一个参数,后面不能再跟 b string这样的参数
    	fmt.Println(a)
    }
    
    package main
    
    import "fmt"
    
    func main()  {
    	s1:= []int{1,2,3,4}
    	a,b :=1,2
    	A(a,b)
    	fmt.Println(a,b)
    	B(s1)
    	fmt.Println(s1)
    
    
    }
    
    func A(a ...int) {
    	//这里传进来的实际上是一个slice,引用类型
    	a[0] = 3
    	a[1] = 4
    	//尽管我们在函数A当中接收到的是一个slice,但是它得到的是一个值拷贝
    	//和直接传递一个slice的区别看函数B
    	fmt.Println(a)
    }
    func B(s []int)  {
    	//这里并不是传递一个指针进去,而是对这个slice的内存地址进行了一个拷贝
    	//这里还可以看到像int型、string型进行常规的参数传进去的话,只是进行了个值拷贝,slice传进去虽然也是拷贝,但是它是内存地址的拷贝
    	s[0] = 4
    	s[1] = 5
    	s[2] = 6
    	s[3] = 7
    	fmt.Println(s)
    	//在这里 我们看到我们在函数B当中的修改,实际上影响到了我们main函数当中的变量s1
    	//如果直接传递一个slice,它的修改就会影响到这个slice的本身
    
    }
    
    PS:值类型和引用类型进行函数传参拷贝是不一样的,一个是拷贝值,一个是拷贝地址
    
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	a := 1
    	A(&a) //这里取出a的地址
    	fmt.Println(a)
    
    }
    
    func A(a *int) { //传递的是指针类型
    	*a = 2 //在操作的时候需要去它的值进行操作,这个时候函数A就可以改变原始a的值
    	fmt.Println(*a)
    }
    
    

    函数类型的使用

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	a := A
    	a() //这个时候是将A的函数类型赋值给a,在go语言当中一切皆是类型啊
    
    }
    func A() {
    	fmt.Println("Func A")
    }
    

    匿名函数的使用

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	a := func() {
    		//此时这个代码块就是一个匿名函数,这个函数本身没有名称,我们将她赋值给a,然后调用
    		fmt.Println("Func A")
    
    	}
    	a() //依然可以打印func A
    }
    

    GO语言当中的闭包

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	f := closure(10)
    	res1 := f(1)
    	fmt.Println(res1)
    	res2 := f(2)
    	fmt.Println(res2)
    
    }
    
    func closure(x int) func(int) int {
    	fmt.Printf("%p 
    ", &x)
    	return func(y int) int {
    		fmt.Printf("%p 
    ", &x)
    		return x + y
    	}
    }
    //这里可以看出3次打印x的地址都是一样的
    
    

    defer

    • defer的执行方式类似其它语言中的析构函数,在函数执行体结束后按照调用顺序的相反顺序逐个执行

    • 即使函数发生严重错误也会执行

    • 支持匿名函数的调用

    • 通常用于资源清理、文件关闭、解锁以及记录时间等操作

    • 通过与匿名函数配合可在return之后修改函数计算结果

    • 如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer时即已经获得了拷贝,否则则是引用某个变量的地址

    • GO没有异常机制,但有panic/recove模式来处理错误

    • Panic可以在任何地方引发,但recover只有在defer调用的函数中有效

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	fmt.Println("A")
    	defer fmt.Println("B")
    	defer fmt.Println("C")
    }
    //PS:打印的结果就是A C B
    
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	for i := 0; i < 3; i++ {
    		//defer fmt.Println(i)
    		defer func() {
    			fmt.Println(i)
    		}() //调用这个函数
    	}
    }
    
    //刚才直接打印的时候,是作为一个参数传递进去,运行到defer的时候是将这个i的值进行了一个拷贝,所以打印的是 2 1 0
    //这种情况下i一直是一个地址的引用,i一直引用的是局部变量的i,在退出这个循环体的时候 i已经变成了3,在main函数return的时候,开始执行defer语句,defer语句的时候i已经变成了3
    

    异常机制

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	A()
    	B()
    	C()
    
    }
    
    func A() {
    	fmt.Println("Func A")
    }
    func B() {
    	defer func() {
    		if err := recover(); err != nil {
    			fmt.Println("Recover in B")
    
    		}
    	}()
    	panic("Panic in B")
    
    }
    func C() {
    	fmt.Println("Func C")
    }
    
    
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var fs = [4]func(){}
    	for i := 0; i < 4; i++ {
    		defer fmt.Println("defer i=", i) //这个i是传递进来的参数,所以是值得拷贝
    		defer func() {
    			fmt.Println("defer_closure i=", i) //这里的i是引用外部的i,所以循环结束后,i变成了4
    		}()
    		fs[i] = func() {
    			fmt.Println("closure i = ", i) //这里也是引用外部的i,所以循环结束后i变成了4
    		}
    	}
    	for _, f := range fs {
    		f()
    	}
    }
    
  • 相关阅读:
    [转]ASP.NET中JSON的序列化和反序列化
    [转]JavaScriptSerializer中日期序列化
    [转]国外英语教学网页
    [转]linq to sql (Group By/Having/Count/Sum/Min/Max/Avg操作符)
    [转]Business Model Canvas(商业模式画布):创业公司做头脑风暴和可行性测试的一大利器
    [转]sql server transaction
    CentOS7安装配置PostgreSQL9.6
    使用struts的同步令牌避免form的重复提交
    Flink初探-为什么选择Flink
    jdbc三种常见用法
  • 原文地址:https://www.cnblogs.com/skymyyang/p/7659775.html
Copyright © 2020-2023  润新知