go语言(3)

  • 函数:在Go中,函数分为:自定义函数、系统函数
    func 函数名 (形参列表)(返回值列表){
    执行语句…
    return 返回值列表
    }
    (1)形参列表:表示函数的输入;形参列表和返回值列表的数据类型可以是值类型也可以是引用类型;
    (2)函数可以有返回值,也可以没有,如果返回多个值时,在接收时,希望忽略某个返回值,则使用_符号表示占位忽略
    (3)如果返回值只有一个,(返回值类型列表)可以不写()。
    (4)基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值.
package main
import (
	"fmt"
)
func test(n1 int){
	n1=n1+10
	fmt.Println("n1=",n1)
}
func main()  {
	n1:=20
	test(n1)
    fmt.Println("main n1=",n1)
}

n1= 30
main n1= 20
(5)如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。

package main
import (
	"fmt"
)
func test(n1 *int){
	*n1=*n1+10
	fmt.Println("n1=",*n1)
}
func main()  {
	n1:=20
	test(&n1)
    fmt.Println("main n1=",n1)
}

n1= 30
main n1= 30
(6)go函数不支持重载。
(7)在go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量,通过该变量可以对函数调用。

package main
import (
	"fmt"
)
func getsum(n1 int,n2 int) (sum int,sub int){
	sum=n1+n2
	sub=n1-n2
	return
}
func main()  {
	a:=getsum
	fmt.Printf("a的类型%T,getsum类型%T\n",a,getsum)
	res,ress:=a(20,30)
	fmt.Println("res= ress=",res,ress)
}

a的类型func(int, int) (int, int),getsum类型func(int, int) (int, int)
res= ress= 50 -10
(8)在go中,函数可以作为形参,并且调用。

package main
import (
	"fmt"
)
func getsum(n1 int,n2 int) int{
	sum:=n1+n2
	return sum
}
func myfun (funvar func(int,int) int,num1 int,num2 int) int{
	return funvar(num1,num2)
}
func main()  {   
	res:=myfun(getsum,20,10)
	fmt.Println("res= ",res)
}

res= 30

(9)为了简化数据类型定义,go支持自定义数据类型。

type myint int  //myint等价于int 
type mysum func(int,int) int   //mysun等价于函数类型func(int,int) int

给int取了别名,在go中myint和int虽然都是int类型,但是go认为myint和int是两个类型。
(10)支持对函数返回值命名

func getsum(n1 int,n2 int) (sum int,sub int){
     sum=n1+n2
     sub=n1-n2
     return
}

(11)go支持可变参数
//支持0到多个参数
func sum(args… int) sum int{
}
//支持1到多个参数
func sum(n1 int,args… int) sum int{
}
说明:args是slice切片,通过args[index]可以访问到各个值;args放后面

package main
import (
	"fmt"
)
func getsum(n1 int,args... int) int{
	sum:=n1
	for i:=0;i<len(args);i++{
		sum+=args[i]
	}
	return sum
}

func main()  {   
	res:=getsum(10,0,-1,90,10,100)
	fmt.Println("res= ",res)
}

(12)函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效。


  • 包的本质实际上是创建不同的文件夹,来存放程序文件;
    (1)go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的;
    (2)包可以控制函数、变量等访问范围,即作用域。
    (3)打包基本语法:package 包名
    引入包的基本语法:import “包的路径”
package util
import "fmt"
//将计算的功能放到一个函数中,然后在使用使用时,调用即可
//为了让其他包的文件使用Cal函数,需要将C大写,类似其他语言的public
func Cal(n1 float64,n2 float64,operator byte) float64{
	var res float64
	switch operator{
	case '+':
		res=n1+n2
	case '-':
		res=n1-n2
	case '*':
		res=n1*n2
	case '/':
		res=n1/n2
	default:
		fmt.Println("操作符号错误")
	}
	return res
}
package main
import (
	"fmt"
	"go_code/project01/util"//导入包
)
func main()  {
	n1:=4.5
	n2:=6.7
	var operator byte='*'
	result:=util.Cal(n1,n2,operator) //引用包,包名.函数名
    fmt.Println("result=",result)
}

在这里插入图片描述
(4)在给一个文件打包时,该包对应一个文件夹,文件的包名通常和文件所在的文件夹名一致,一般为小写字母。
(5)在import 包时,路径从src下开始,不用带src,编译器会自动从src下开始引入。
(6)如果包名较长,Go支持给包取别名,取别名后,原来的包名不能使用

package main
import (
	"fmt"
	u "go_code/project01/util"//取别名
)
func main()  {
	n1:=4.5
	n2:=6.7
	var operator byte='*'
	result:=u.Cal(n1,n2,operator) //引用包
    fmt.Println("result=",result)
}

(7)在同一包下,不能有相同的函数名,也不能有相同的全局变量名,否则报重复定义。(同一个文件夹下两个go文件也不可以)
(8)如果要编译一个可执行程序文件,就需要将这个包声明为main,即package main.但是写一个库,包名可以自定义。
编译的指令,在项目目录下,编译路径也不需要带src,编译器会自动带。此外,可执行文件还可以指定名字和目录。

go build -o src/my.exe go_code/project01/main
  • 递归函数:一个函数在函数体内又调用了本身,我们称为递归调用。
    (1)执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
    (2)函数的局部变量是独立的,不会相互影响。
    (3)当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁;同时当函数执行完毕或者返回时,该函数本身也会被系统销毁。

  • init函数
    每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被go运行框架调用,也就是说init会在main函数前被调用。
    (1)如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程是变量定义->init函数->main 函数
    (2)init函数最主要的作用,就是完成一些初始化的工作。
    (3)如果main.go和util.go都含有变量定义,init函数时,执行的流程在这里插入图片描述

  • 匿名函数
    go支持匿名函数,如果某个函数只是希望使用一次,可以考虑使用匿名函数。匿名函数也可以实现多次调用。
    匿名函数使用方式1:再定义匿名函数时就直接调用。
    匿名函数使用方式2:将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数。

package main
import (
	"fmt"
)

func main()  {   
	res1:=func(n1 int,n2 int) int{
		return n1+n2
	}(10,20)

	fmt.Println("res1= ",res1)

	a:=func(n1 int,n2 int) int{
		return n1-n2
	}

	res2:=a(10,30)
    fmt.Println("res2= ",res2)

}

res1= 30
res2= -20
全局匿名函数:如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以再程序有效。

package main
import (
	"fmt"
)
var (
	//fun1是一个全局匿名函数
	fun1=func(n1 int,n2 int) int{
		return n1*n2
	}
)
func main()  {   
	res:=fun1(4,9)
    fmt.Println("res= ",res)
}

res= 36

  • 闭包
    闭包就是一个函数和与其相关的应用环境组合的一个整体(实体)
package main
import (
	"fmt"
)
func AddUpper() func (int) int{
	var n int =10
	return func(x int) int{
		n=n+x
		return n
	}
}

func main()  {   
	//使用前面的代码
	f:=AddUpper()
    fmt.Println(f(1))  //11
    fmt.Println(f(2))  //13
    fmt.Println(f(3))  //16
}

(1)AddUpper()是一个函数,返回的数据类型是func (int) int
(2)AddUpper()中,返回的是一个匿名函数,但是这个函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成闭包。
(3)可以理解为闭包是类,函数是操作,n是字段。函数和它使用到n构成闭包。
(4)当反复调用f函数时,因为n是初始化一次,因此每调用一次就进行累计。
(5)搞清楚闭包的关键,就是要分析出返回的函数使用(引用)到哪些变量,因为函数和它引用的变量共同构成闭包。
(6)闭包的使用

package main
import (
	"fmt"
	"strings"
)
//makeSuffix(suffix string)可以接收一个文件后缀名(.jpg),并返回一个闭包
//调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg),则返回 文件名.jpg,如果已经有后缀,则返回原文件名
func makeSuffix(suffix string)  func (string) string{ 
	return func(name string) string{
		//如果没有指定后缀则加上,否则返回原来的名字
		if !strings.HasSuffix(name,suffix){
			return name+suffix
		}
		return name 
	}
}

func main()  {   
	f2:=makeSuffix(".jpg")
	fmt.Println("文件名处理后=",f2("winter"))
    fmt.Println("文件名处理后=",f2("bird.jpg"))
}

文件名处理后= winter.jpg
文件名处理后= bird.jpg

  • defer
    在函数中,需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时释放资源,go提供defer(延时机制)
package main
import (
	"fmt"
)

func sum(n1 int,n2 int) int{ 
	defer fmt.Println("ok1 n1=",n1)//3
	defer fmt.Println("ok2 n2=",n2)//2

	res:=n1+n2
	fmt.Println("ok3 res=",res)//1
	return res
}

func main()  {   
	res:=sum(10,20)
    fmt.Println("res=",res) //4
}

ok3 res= 30
ok2 n2= 20
ok1 n1= 10
res= 30
(1)当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)
(2)当函数执行完毕后,再从defer 栈,按照先入后出的方式出栈,执行
(3)在defer将语句放入到栈时,也会将相关的值拷贝同时入栈。

package main
import (
	"fmt"
)

func sum(n1 int,n2 int) int{ 
	defer fmt.Println("ok1 n1=",n1)//3
	defer fmt.Println("ok2 n2=",n2)//2
	n1++
	n2++
	res:=n1+n2
	fmt.Println("ok3 res=",res)//1
	return res
}

func main()  {   
	res:=sum(10,20)
    fmt.Println("res=",res) //4
}

ok3 res= 32
ok2 n2= 20
ok1 n1= 10
res= 32
(4)当函数执行完毕后,defer可以及时的释放函数创建的资源

//关闭文件资源
func test(){
	file=openfile(文件名)
	//当函数结束时,执行file.close()
	defer file.close()
	//...
}
package main
import (
	"fmt"
)
//释放数据库资源
func test(){
	connect=openDatabse()
	//当函数结束时,执行connect.close()
	defer connect.close()
	//...
}

在go中,创建资源后,比如打开了文件、获取了数据库的链接,或者是锁资源,可以执行defer connect.close()等。在defer后,可以继续使用创建的资源。当函数完毕后,系统会依次从defer栈中,取出语句,关闭资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值