go学习记——(9)函数

函数

  1. 函数的形参列表可以是多个,返回值列表也可以是多个。
  2. 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不回影响到原来的值。
  3. 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。
  4. Go函数不支持传统的重载。
包的三大作用:
  1. 区分相同名字的函数、变量等标识符;
  2. 当程序文件很多时,可以很好的管理项目
  3. 控制函数、变量等访问范围,即作用域
包的相关说明
  1. 打包的基本语法
package 包名
  1. 引入包的基本语法
import "包的路径" // 路径从src后面开始写
improt(
    别名 "包路径"
    )

函数递归

函数递归调用需要遵守的重要原则:

  1. 执行一个函数时,就创建一个新的受保护的独立空间(新函数栈);
  2. 函数的局部变量是独立的,不会相互影响;
  3. 递归必须向退出递归的条件逼近,否则就是无限递归;
  4. 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁。
package main

import (
	"fmt"
)
// 斐波那契数列
func fib(n int) (f int) {
	if n <= 2 {
		f = 1
	} else {
		f = fib(n-1) + fib(n-2)
	}
	return f
}
func main() {
	a := fib(4)
	fmt.Println(a)
}

函数细节
  1. 在go中,函数也是一种数据类型
  2. 在go中,函数可以作为形参,并且调用
  3. 给数据类型取别名,但go认为别名和原本的数据类型是两个类型,可以应用于函数参数类型
type myInt int
var num myInt
num1 = 40
type myFun func(int,int) int

  1. Go支持可变参数
    1.args是slice切片,通过args[index]可以访问到各个值。
// 支持0到多个参数
func sum(args...int) sum int {

}
// 支持1到多个参数
func sum(n1 int, args ...int) (sum1 int) {
	sum1 = n1
	for _, val := range args {
		sum1 += val
	}
	return
}
init函数

每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用。

  1. 如果一个文件同时包含局变量定义、init函数和main函数,则执行的流程是:全局变量定义=>init函数=>main函数
匿名函数

使用方式:

  1. 在定义匿名函数时就直接调用。
res := func(n1, n2 int) int {
	return n2 + n1
}(1, 2)
  1. 将匿名函数赋值给一个变量(函数变量),再通过该变量来调用匿名函数。
  2. 全局匿名函数,将匿名函数赋值给一个全局变量,在全局均可调用这个函数。
闭包
func AddUpper() func(int) int {
	var n int = 10
	return func(x int) int {
		n += x
		return n
	}
}

经典闭包应用

// 给文件加后缀
func makeSuffix(s string) func(string) string {
	var suffix string = s
	return func(name string) (newName string) {
		if strings.HasSuffix(name, suffix) {
			newName = name
		} else {
			newName = name + suffix
		}
		return
	}
}
func main() {
	f := makeSuffix(".jpg")
	newName := f("www.jpg")
	fmt.Println(newName)
}
defer

在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)。

  1. 当go执行倒一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入倒一个栈中,然后继续执行函数的下一个语句。
  2. 当函数执行完毕后,再从栈中,依次从栈顶取出语句执行(注:遵守栈,先入后出的机制)。
  3. 在defer将语句放入栈时,也会将相关的值拷贝同时入栈。
函数参数传递方式
  1. 值类型:基本数据类型int系列,float系列,bool,string,数组,结构体struct。
  2. 引用类型:指针,slice切片,map,管道chan,interface等都是引用类型。

其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据两小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。

内置函数

  1. len()
  2. new():new(int)返回一个int指针,*指针可以取到值。(用来分配内存,主要用来分配值类型)
  3. make():(用来分配内存,主要用来分配引用类型)
  4. cap(slice):容量

错误处理

  1. Go中引入的处理方式为:defer,panic,recover
  2. 这几个异常的使用场景可以这么简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。
使用defer+recover处理异常
package main

import (
	"fmt"
	"time"
)

func test() {
	defer func() {
		err := recover() //recover()内置函数,可以捕获到异常
		if err != nil {
			fmt.Println("err=", err)
		}
	}()
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println("res=", res)
}
func main() {
	test()
	for {
		fmt.Println("main()下面代码……")
		time.Sleep(time.Second)
	}
}

自定义错误

Go程序中,也支持自定义错误,使用errors.New 和panic内置函数。

  1. errors.New(“错误说明”),会返回一个error类型的值,表示一个错误
  2. panic内置函数,接受一个interface{}类型的值(也就是任何值了)作为参数。可以接收error类型的变量,输出错误信息,并退出程序。
func readConfig(name string) (err error) {
	if name == "config.ini" {
		return nil
	} else {
		// 返回一个自定义错误
		return errors.New("读取文件错误")
	}
}
func test2() {
	err := readConfig("c")
	if err != nil {
		// 输出错误并终止程序
		panic(err)
	}
}

版权声明:本文为weixin_38117824原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。