Golang学习+深入(七)-函数

目录

一、概述

1、函数

1.1、函数-递归调用

1.2、init函数

1.3、匿名函数

1.4、闭包

1.5、函数中-defer

1.6、函数参数的传递方式

2、包

3、字符串中常用的函数


一、概述

1、函数

  1. 为完成某一功能指令(语句)的集合,称为函数。
  2. 在Go中,函数分为:自定义函数,系统函数
  3. 函数解决代码冗余
基本语法:
func 函数名 (形参列表) (返回值类型列表) {
    执行语句...
    return 返回值列表
}
1.形参列表,表示函数的输入
2.函数中的语句,表示为了实现某一功能代码块
3.函数可以有返回值,也可以没有
4.Go函数支持返回多个值,这一点是其他编程语言没有的。
5.如果返回多个值,在接收时,希望忽略某个返回值,则使用 _符号表示占位忽略
6.如果返回值只有一个,(返回值类型列表)可以不写()
7.形参列表和返回值类型列表的数据类型可以是值类型和引用类型
8.函数名的命名规范,首字母不能是数字,首字母大写该函数可以被本包和其他包文件使用,类似public,反之小写,类似private。
9.函数中的变量是局部的,函数外不生效
10.基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
11.如果希望函数内的变量能够修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用。
12.Go函数不支持重载
13.在Go中,函数也是一种数据类型,可以赋值给一个变量,该变量就是一个函数类型的变量,通过该变量可以对函数调用
14.函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用!
15.为了简化数据类型定义,Go支持自定义数据类型
    基本语法:
    type  自定义数据类型名 数据类型 //理解:相当于一个别名
    例:type myInt int //这时myInt等价int来使用
16.支持对函数返回值命名
    例:
    func getSum(n1 int,n2 int) (sum int) {
	sum = n1+n2
	return 
    }
17.使用 _标识符,忽略返回值
18.Go支持可变参数
    func sum(args... int) sum int{//支持0-多个参数
    }
    func sum(n1 int,args... int) sum int{//支持1-多个参数
    }
    说明:args是slice,通过args[index]可以访问到各个值
    如果一个函数的形参列表中有可变参数,则可变参数需要放在形参列表最后。
  1. 在调用一个函数时,会给该函数分配一个新的空间,编译器会通过自身的处理让这个新的空间和其他的栈空间区分开来
  2. 在每个函数对应的栈中,数据空间是独立的,不会混淆
  3. 当一个函数调用完毕(执行完毕)后,程序会销毁这个函数对应的栈空间。
package main
import (
	"fmt"
)
func getSum(n1 int,n2 int) int {
	return n1 + n2
}
func getSumByFunc(funcVar func(int,int) int,n1 int,n2 int) int {
	return funcVar(n1,n2)
}
func main() {
	a :=getSum
	fmt.Printf("a的类型%T,getSum的类型%T\n",a,getSum)
	res := a(10,20)//等价于getSum(10,20)
	fmt.Println("res=",res)
	res2 := getSumByFunc(getSum,20,30)
	fmt.Println("res2=",res2)
}

1.1、函数-递归调用

  1. 一个函数在函数体内又调用了本身,我们称为递归调用。
  2. 递归必须向退出递归的条件逼近,否则就是无限递归。
  3. 当一个函数执行完毕后,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当函数执行完毕或返回时,该函数本身也会被销毁。

1.2、init函数

  1. 每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用。通常在init函数中完成初始化工作。
  2. 如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程是:变量定义-->init函数-->main函数
  3. init函数最主要的作用就是完成一些初始化的工作.
package main
import (
	"fmt"
)
var age = test()
func test() int{
	fmt.Println("test()--age")
	return 10
}
func main() {
	fmt.Println("main()")
}
func init() {
	fmt.Println("init()")
}
//输出结果:
//test()--age
//init()
//main()
面试题:
案例:main.go引入utils.go,都含有变量定义,init函数时执行的流程?
1.utils.go 变量定义
2.utils.go init函数
3.main.go 变量定义
4.main.go init函数
5.main.go main函数

1.3、匿名函数

  1. Go支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。
  2. 使用方式1:在定义匿名函数时就直接调用。
  3. 使用方式2:将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数
  4. 全局匿名函数:如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在整个程序有效。
package main
import (
	"fmt"
)

var (
	//全局匿名函数
	fun1 = func (n1 int ,n2 int) int {
		return n1 + n2
	}
)

func main() {
	
	//方式1:
	res := func (n1 int,n2 int) int{
		return n1 + n2
	}(10,20)
	fmt.Println("res=",res)

	//方式2:
	a := func (n1 int,n2 int) int{
		return n1 - n2
	}
	res1 := a(20,15)
	fmt.Println("res1=",res1)
	
	res2 := fun1(20,30)
	fmt.Println("res2=",res2)
}

1.4、闭包

闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

package main
import (
	"fmt"
)
//累加器
func add() func (int)int{
	var n int = 10
	return func (x int) int{
		n = n + x
		return n
	}
}

func main() {
	f := add()
	fmt.Println(f(1))//11
	fmt.Println(f(2))//13
}

说明: add是一个函数,返回的数据类型是fun (int) int 闭包的说明

    var n int = 10
    return func (x int) int{
            n = n + x
            return n
    }
    //返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数和n就形成一个整体,构成闭包。
    //可以理解:闭包是类,函数是操作,n是字段函数和使用的n构成闭包
    //当我们反复调用函数f时,因为n是初始化一次,因此每调用一次就进行累计。
    //闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包。
    //闭包可以保留上次引用的某个值,可以反复使用

1.5、函数中-defer

  1. 在函数中,程序员经常需要创建资源(比如:数据库连接,文件句柄,锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)。
  2. 当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈中,然后继续执行函数下一个语句
  3. 当函数执行完毕后,再从defer独立栈中,按照先入后出的方式出栈,执行
  4. 在defer将语句放入到栈时,也会将相关的值拷贝同时入栈。
  5. defer最主要的价值在,当函数执行完毕后,可以及时的释放函数创建的资源。
package main
import (
	"fmt"
)

func main() {
	sum(10,20)
}

func sum(n1 int ,n2 int) int{
	defer fmt.Println("n1=",n1)
	defer fmt.Println("n2=",n2)
        n1++
        n2++
	res := n1+n2
	fmt.Println("res=",res)
	return res
}

1.6、函数参数的传递方式

  1. 两种传递方式:1、值传递 2、引用传递
  2. 其实不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。
  3. 值类型:基本数据类型int系列,float系列,bool,string,数组和结构体struct
  4. 引用类型:指针、slice切片、map,管道chan、interface等都是引用类型
  5. 值类型:变量直接存储值,内存通常在栈中分配
  6. 引用类型:变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。
  7. 如果希望函数内的变量能够修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。

2、包

  1. 包的本质实际上就是创建不同的文件夹,来存放程序文件。
  2. go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的。

包的三大作用

  1. 区分相同名字的函数、变量等标识符
  2. 当程序文件很多时,可以很好的管理项目
  3. 控制函数、变量等访问范围,即作用域
基本语法:
package 包名
引入包基本语法
import "包的路径"
  1. 文件的包名通常和文件所在的文件夹名一致,一般为小写字母。
  2. 当一个文件要使用其他包函数或变量时,需要先引入对应的包
  3. 在import包时,路径从$GOPATH的src下开始,不用带src,编译器会自动从src下开始引入。
  4. 为了让其他包的文件,可以访问到本包的函数,则该函数名的首字母需要大写,类似其他语言的public,这样才能跨包访问
  5. 在访问其他包函数时,其语法是 包名.函数名
  6. 如果包名较长,Go支持给包取别名,注意,取别名后,原来的包名就不能使用了。
package main
import (
	f "fmt"
)
func main() {
	f.Println("Hello World!")
}
  1. 在同一包下,不能有相同的函数名,否则报重复定义。
  2. 如果你要编译成一个可执行文件,就需要将这个包声明为main,即package main

3、字符串中常用的函数

1、统计字符串的长度,按字节 len(str)
len()内建函数,在godoc文档的builtin下  func len(v Type) int:内建函数len返回 v 的长度,这取决于具体类型
2、字符串遍历,同时处理有中文的问题r := []rune(str)
3、字符串转整数: n,err :=strconv.Atoi("45")
4、整数转字符串: str =strconv.Itoa(12345)
5、字符串转[]byte:var bytes =[]byte["hello go"]
6、[]byte转字符串: str=string([]byte{97,98,99})
7、10进制转2,8,16进制:str=strconv.FormatInt(123,2) //2->8,16
8、查找子串是否在指定的字符串中:strings.Contains("seafood","foo")  //true
9、统计一个字符串有几个指定的子串: strings.Count("ceheese","e") //4
10、不区分大小写的字符串比较(==是区分字母大小写的):fmt.Println(strings.EqualFold("abc","Abc"))  //true
11、返回子串在字符串第一次出现的index值,如果没有返回-1:strings.Index("NLT_abc","abc")  //4
package main
import (
	"fmt"
	"strconv"
	"strings"
)

func main(){
	// 1、统计字符串的长度,按字节
	str1 := "hello北"
	fmt.Println("str1 len=",len(str1))
	// 2、字符串遍历,同时处理有中文的问题
	str2 := "hello北京"
	r := []rune(str2)
	for i :=0;i
12、返回子串在字符串最后一次出现的index,如没有返回-1:strings.LastIndex("go golang","go")
13、将指定的子串替换成另外一个子串:strings.Replace("go go hello","go","go语言",n)  //n可以指定你希望替换几个,如果n=-1表示全部替换
14、按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组:strings.Split("hello,world,ok",",")
15、将字符串的字母进行大小写的转换:strings.ToLower("Go") //strings.ToUpper("Go") //GO
16、将字符串左右两边的空格去掉,strings.TrimSpace(" tn a lone go lang   ")
17、将字符串左右两边的空格去掉:strings.Trim("! hello! "," !") //将左右两边!和" "去掉
18、将字符串左边指定的字符去掉:strings.TrimLeft("! hello! "," !") //将左边!和" "去掉
19、将字符串右边指定的字符去掉:strings.TrimRight("! hello! "," !") //将右边!和" "去掉
20、判断字符串是否以指定的字符串开头:strings.HasPrefix("ftp://192.168.10.1","ftp") //true
21、判断字符串是否以指定的字符串结束:strings.HasSuffix("ftp://192.168.10.1sss","sbd") //false
package main
import (
	"fmt"
	"strings"
)

func main(){
	// 12、返回子串在字符串最后一次出现的index,如没有返回-1
	str12 := strings.LastIndex("go golang","go")
	fmt.Printf("str12=%v\n",str12)
	// 13、将指定的子串替换成另外一个子串
	str13 := strings.Replace("go go hello","go","go语言",-1)  //n可以指定你希望替换几个,如果n=-1表示全部替换
	fmt.Printf("str132=%v\n",str13)
	// 14、按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组
	str14 := strings.Split("hello,world,ok",",")
	fmt.Printf("str14=%v\n",str14)
	// 15、将字符串的字母进行大小写的转换
	str15 := strings.ToLower("Go") //strings.ToUpper("Go") //GO
	fmt.Printf("str15=%v\n",str15)
	// 16、将字符串左右两边的空格去掉
	str16 := strings.TrimSpace(" tn a lone go lang   ")
	fmt.Printf("str16=%v\n",str16)
	// 17、将字符串左右两边的空格去掉
	str17 := strings.Trim("! hello! "," !") //将左右两边!和" "去掉
	fmt.Printf("str17=%v\n",str17)
	// 18、将字符串左边指定的字符去掉
	str18 := strings.TrimLeft("! hello! "," !") //将左边!和" "去掉
	fmt.Printf("str18=%v\n",str18)
	// 19、将字符串右边指定的字符去掉
	str19 := strings.TrimRight("! hello! "," !") //将右边!和" "去掉
	fmt.Printf("str19=%v\n",str19)
	// 20、判断字符串是否以指定的字符串开头
	fmt.Println(strings.HasPrefix("ftp://192.168.10.1","ftp"))
	// 21、判断字符串是否以指定的字符串结束
	fmt.Println(strings.HasSuffix("ftp://192.168.10.1sss","sbd"))
}

干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!

你可能感兴趣的:(golang,学习,算法)