Go语言自学笔记(二)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_18800771/article/details/96829398

Go语言函数:

函数定义的格式:

func fun(/*参数列表*/)(/*返回值列表*/){
    //函数体
    //返回语句
}

无参数无返回值的函数定义及调用:

package	main
import "fmt"
func fun(){		
	fmt.Println("函数运行")	
}		
func main(){
	fun()
}

需要注意的是:

1.程序从入口执行,函数必须先被定义才能被调用,需要在主函数中调用函数:函数名()

2.自定义函数在主函数前后无区别,函数被定义但是没有调用不会报错。

3.单独程序源代码内函数名大小写无区别

有参数无返回值的函数定义及调用:普通参数列表

func fun(a int){
	fmt.Println("函数参数",a)
}
func main(){
	fun(100)
}

需要注意的是:

1.定义函数时在函数名的()内定义的参数叫形参。

2.函数不能重名/重复定义,参数定义必须在参数列表内且不需要关键字,定义不可以放在函数体内。同时,参数的赋值只能在函数体内,不可以放在参数列表,否则将会报错。

3.函数调用时函数名(所需参数),调用函数传递的所需参数叫实参。

4.参数传递只能由实参传递给形参,不可以反向传递(单向传递)。

含有多个参数的函数的定义及调用:

func fun(a int,b int){        //参数类型相同可以简写为(a,b int)
	fmt.Println("函数参数",a,b)
}
func main(){
	fun(100,200)
}

需要注意的是:参数列表有几个参数调用的时候就要传递几个参数,否则会报错:不够参数调用。

有参数无返回值的函数定义及调用:不定参数列表

func fun(args ...int){
	fmt.Println("函数参数",args)
}
func main(){
	fun(100,200,300)
}

需要注意的是:

1.不定参数的类型:...int类似这样的类型叫做不定参数类型(...type)。

2.传递的实参可以是0-n多个

3.使用不定参数时,內建函数len(args)可以读取用户传递的参数的个数。

不定参数的使用:

for循环使用:在不定参数列表的函数的函数体内使用

for i:=0;i<len(args);i++{
	fmt.Printf("参数%d的内容为%d\n",i,args[i])
}

迭代使用:在不定参数列表的函数的函数体内使用

for i,data:=range args{
	fmt.Printf("参数%d的内容为%d\n",i,data)
}

需要注意的是:

1.不定参数一定要放在形参中的最后一个参数,否则报错。

func fun(a int,args ...int){}

2.此时固定参数a是必须传参的,不定参数可根据需求传参数。

不定参数的传递:我们可以在函数内将不定参数传给另外一个函数

func myfunc (tmp ...int){	
}
func test(args ...int){
	myfunc(args...)    //全部元素传递给myfunc
	myfunc(args[:2]...)    //args[2]前(不包括)进行传递
	myfunc(args[2:]...)    //args[2]后(包括)进行传递
}
func main(){
    test(1,2,3,4)
}

无参数有返回值的函数定义:

单一返回值具有多种写法:

写法一:

func myfunc() int {		
	return 100
}

需要注意的是:

1.只有一个返回值时可以不声明变量名称,省略括号。

2.有返回值的函数需要用return来中断函数并返回相应的量。

写法二:

func myfunc() (result int) {
	return 100
}

此时我们也可以为返回值起一个变量名,并将代码优化为:常用方

func myfunc() (result int) {
	result=100
	return 
}

此方法为返回值起名,这是Go语言的一种推荐写法,以清晰为主。

注意:返回整个返回值列表,return之后不用写内容。

无参数有返回值的函数的调用:

func main(){
	var a int
	a=myfunc()    //赋值接受返回值
	b:=myfunc()    //自动推导接受返回值
}

无参数返回多个返回值的函数的声明:Go语言独特之处,不同于其他语言的单一返回值局限

传统写法:

func myfunc() (int,int,int) {
	return 1,2,3
}

Go语言推荐的写法:

func myfunc() (a int,b int,c int) {	//也可以写为(a,b,c int)
	a,b,c=1,2,3		//多重赋值
	return 
}

无参数返回多个返回值的函数的调用:

func main(){
    a,b,c:=myfunc()
    fmt.Printf("第一个参数:%d\n第二个参数:%d\n第三个参数:%d\n",a,b,c)
}

此时如果对返回值进行输出,推荐使用格式化输出更为清晰方便

有参数也有返回值的函数声明及调用:例求两个数的最大值

func maxandmin(a,b int)(max,min int){
	if a>b{
		max=a 
		min=b 
	}else{
		max=b 
		min=a 
	}
	return 
}
func main(){
	max,min:=maxandmin(10,20)
}

此时,如果我们想单独接收最大值或最小值,也可以用匿名变量来缺省某个返回值:

max,_:=maxandmin(10,20)

普通函数的调用流程:一组嵌套调用--先调用后返回,先进后出

package main
import "fmt"
func fun2(x int) (a int) {
	a=x+1
	fmt.Println("函数2运行,当前a的值是", a)
	fmt.Println("函数2运行结束,当前a的值是", a)
	return
}
func fun1(x int) (a int) {
	a=x+1
	fmt.Println("函数1运行,当前a的值是", a)
	a = fun2(a)
	fmt.Println("函数1运行结束,当前a的值是", a)
	return
}
func main() {
	a := 0
	fmt.Println("主函数运行,当前a的值是", a)
	a = fun1(a)
	fmt.Println("主函数运行结束,当前a的值是", a)
}

运行结果:

主函数运行,当前a的值是 0
函数1运行,当前a的值是 1
函数2运行,当前a的值是 2
函数2运行结束,当前a的值是 2
函数1运行结束,当前a的值是 2
主函数运行结束,当前a的值是 2

当发现每个函数都执行了类似的功能,此时,我们运用这种特点改写了函数递归调用:函数调用自己本身:

package main
import "fmt"
func test(a int) {
	if a == 2 {
		fmt.Printf("递归a=%d\n", a)
		return //终止函数,否则将无限递归
	}
	fmt.Printf("递归a=%d开始\n", a)
	test(a + 1)
	fmt.Printf("递归%d结束\n", a)
}
func main() {
	a := 0
	test(a)
}

运行结果:

递归a=0开始
递归a=1开始
递归a=2
递归1结束
递归0结束

递归函数的应用:数字累加的实现(1-100)

传统方法:为for循环封装函数:

func test01() (sum int) {
	for i := 1; i <= 100; i++ {
		sum += i
	}
	return
}
func main() {
	sum := test01()
	fmt.Println(sum)
}

递归方法:

倒序累加:

func test02(i int) int {
	if i == 1 {
		return 1
	}
	return i + test02(i-1)
}
func main() {
	sum := test02(100)
	fmt.Println(sum)
}

正序累加:

func test03(i int) int {
	if i == 100 {
		return i
	}
	return i + test03(i+1)
}
func main() {
	sum := test03(1)
	fmt.Println(sum)
}

Go语言函数类型:

传统调用函数方法:函数名(参数列表)

func Add(a,b int ) int {
	return a+b
}
func minus(a,b int) int{
	return a-b
}
func main(){
    result:=Add(1,1)
}

我们可以将函数也看做一种数据类型,通过type给函数起一个函数类型名称,但要求与函数有同样的参数列表和返回值,即可通过函数类型变量赋值。

type FuncType func(int,int)int    //没有函数名字以及大括号,FuncType是一种自定义函数类型

函数类型变量的定义及赋值:

func main(){
    var fTest FuncType    //声明一个函数类型变量fTest
    fTest = Add    //将函数入口赋值给变量
    result:=fTest(1,1)    //等价于result:=Add(1,1)
}

需要注意的是:函数名就是函数的入口地址,函数类型则类似于c语言中的函数指针

多态思想的主要体现:回调函数--函数的一个参数是函数类型

计算函数实现四则运算:

func Calc(a,b int, fTest FuncType) (result int){
	result=fTest(a,b)		//函数没有实现
	return 
}

需要注意注意的是,此时的fTest具体函数并没有实现。对于此计算函数,先有框架,再实现功能。体现了多态性:多种形态调用,调用同一个接口,可以实现不同的功能--四则运算。

计算函数的调用:

func main(){
    result:=Calc(1,1,Add)
}

此处类似于Python中的字典或switch调用函数的使用

此方法的优势:

不用先定义并实现后调用,此函数可以先预留出调用的空间,不需要立即实现传递的参数函数,给函数更多的拓展空间,具有独特的使用更多函数功能的优势。传统写法没有办法实现多态,需要立即实现功能。

匿名函数与闭包:没有函数名字,可以捕获外界的变量。

匿名函数的定义:

定义及手动调用:

func main(){
    f1:=func(){		//自动推导类型,较为常用
        fmt.Println("匿名函数")
    }
    f1()
}

函数别名调用:此方法不常用

type FuncType func()
var f2 FuncType
f2=f1
f2()

定义匿名函数同时自动调用:

func(){
    fmt.Println("匿名函数")
}()		//此()代表自动调用此匿名函数--传递参数括号

带有参数的匿名函数手动调用:

f3:=func(i,j int){
    fmt.Println("匿名函数参数:",i,j)
}
f3(10,20)

带有参数的匿名函数自动调用:

func(i,j int){
    fmt.Println("匿名函数参数:",i,j)
}(10,20)

带有参数和返回值的匿名函数自动调用:

x,y:=func(i,j int)(max,min int){
    //函数体,将参数最大值赋给max,参数最小值赋给min。详细代码省略
    return
}(10,20)

闭包捕获外部变量的特点:捕获外部的变量并可以在闭包内部修改变量值,在外部修改也生效

func main(){
    a:=10
    func(){
	    a=11
	    fmt.Println(a)
    }()
    fmt.Println(a)
}

闭包以引用方式来捕获外部变量:

普通函数:

func test01 () int{
	var x int
	x++
	return x*x
}

此函数在主函数中每次调用,返回的结果都是1:每次调用,结果相同。因为此函数被调用时x才分配空间并初始化为0,函数调用完毕后x自动释放。

若让函数的返回值是一个匿名函数,返回一个函数类型:闭包

func test02() func() int{
	var x int
	return func() int{
		x++
		return x*x
	}
}
func mian(){
    f:=test02()
}

此时,函数的返回值是一个匿名函数,返回了一个函数类型,通过f来调用返回的匿名函数--闭包函数。

此函数在出函数中每次调用,返回的结果是不同的1,4,9,16....

我们可以得出结论:闭包并不关心捕获的变量和常量是否超出作用域,只要闭包还在使用这些量,他们就还会存在不会被释放和重置。即:闭包里变量的生命周期不是由他的作用域决定的。

延迟调用关键字defer:只能放在函数的内部,可以完成一些清理和关闭的作用,会在main函数结束前运行。

func main(){
	defer fmt.Println("打印语句2")
	fmt.Println("打印语句1")
}

此时输出结果为:

打印语句1
打印语句2

多个defer共同作用时:先执行没有defer的语句,之后按defer先写的后调用来执行。函数等的中途错误并不会导致defer执行中断。

defer与匿名函数结合使用:

defer func(a,b int){
    fmt.Println("匿名函数参数:",a,b)
}(100,200)

需要注意的是:如果此类函数传递了参数,传递参数的顺序在整个程序的顺序结构中不会受到影响,但此类函数会在主函数结束前才被调用。

Go语言中获取命令行参数:

package main
import "os"
func main(){
    list:=os.Args
}

此时可以通过內建函数来获取用户输入参数的个数:

n:=len(list)

但需要注意的是:接受用户传递的参数是以字符串方式接受,运行命令也被视作一个参数。

同时如果我们想获取用户的参数,也可以通过以下方式:

for循环方法:

for  i:=0;i<n;i++{
    fmt.Printf("用户第%d个参数:%d\n",i,list[i])
}

迭代方法:

for i,data:=range list{
    fmt.Printf("用户第%d个参数:%d\n",i,data)
}

Go语言中的变量规则:

局部变量:定义在{}里或所属于某个语句块如if、for等的就是局部变量(作用域,变量的作用范围),只在大括号里或某个语句块内有效。执行到定义局部变量才会分配空间,离开作用域自动释放。

全局变量:定义在函数外部的变量就是全局变量,在程序的任何地方都能够使用。

当局部变量与全局变量同名时有以下规则:不同作用域允许定义同名变量,但使用变量采取就近原则,使用变量的语句所在的变量的作用域内的变量即是就近的变量。

Go语言的工作区:

1.Go语言代码必须放在工作区,否则无法导入包。

2.封装更具备模块化有利于代码的维护和调用。

3.源代码必须要放在src目录下。

4.导入包会从GOPATH导入。

导入包的方法:

传统方法:

import "fmt"
import "os"

常用写法:

import(
	"fmt"
	"os"
)

(点).操作:

import . "fmt"

使用此方法,调用函数无需通过包名,但由于个人习惯可能会导致重名问题。

别名方法:

import io "fmt"

使用此方法,调用函数时的fmt则可以变更为io。

忽略包方法:

import _ "fmt"

使用此方法是为了引入该包,但不使用包内的函数,只为盗用包内的init函数(后文有阐述)。

init函数的介绍:init函数相当于初始化函数

导入包时,执行包内init函数之后再来执行主函数。

若本身也有init函数,则先执行导入的包的init,在执行本身init,最后执行main。

Go语言的工程管理:

同文件夹下的工程管理:

1..分文件编程(多个源文件),必须放在工程目录下src目录(自写)。

2.设置GOPATH环境变量。

电脑属性>高级系统设置>环境变量>系统变量GOPATH设置src目录内工程文件夹(工程src目录所在目录)

3.同一个目录包名必须一样,只能有一个程序入口。

src内:main.go(package main) test.go(package main)

4.go env查看go相关环境路径。

5.同一个目录调用其他文件的函数可直接调用无需包名引用。

不同级目录文件夹下的工程管理:

1.不同目录包名不同

src内:main.go(package main)

src/cacl内: calc.go(package calc)

2.调用不同包里面的函数格式:包名.函数名()  包名需要import导入

3.临时配置:liteide下编译配置自定义GOPATH 工程src所在目录

4.如果调用别的包的函数,函数名字是小写则无法被调用,能被调用的函数必须首字母大写。

如果有多个文件或多个包:

1.配置GOPATH环境变量,配置src目录的绝对路径---工程文件夹。

2.自动生成bin和pkg,则需要使用go install命令。

3.配置GOBIN

在工程目录下:

1.在系统变量内新建GOBIN,变量值则为工程目录下的bin目录。

2.工程src目录下在命令行使用go install,自动生成bin与pkg。

src:存放源代码

bin:存放可执行程序

pkg:存放平台相关的库

猜你喜欢

转载自blog.csdn.net/qq_18800771/article/details/96829398