zoukankan      html  css  js  c++  java
  • 08-Go语言之函数

    内容目录

    • 函数基础
      • 定义
      • 调用
      • 参数
      • 返回值
    • 高阶函数
      • 作用域
      • 函数作为变量
      • 函数作为参数
      • 函数作为返回值
      • 匿名函数
      • 闭包
    • defer语句

    内容详细

    函数基础:定义

    • Go语言中支持函数、匿名函数和闭包,并且函数在Go语言中属于“一等公民”。

    • Go语言中定义函数使用func关键字,具体格式如下:

      func 函数名(参数)(返回值){
          函数体
      }
      

      其中:

      • 函数名:由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名称不能重名(包的概念详见后文)。
      • 参数:参数由参数变量和参数变量的类型组成,多个参数之间使用,分隔。
      • 返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用,分隔。
      • 函数体:实现指定功能的代码块。

    函数基础:调用

    • 定义了函数之后,我们可以通过函数名()的方式调用函数。

    • 注意,调用有返回值的函数时,可以不接收其返回值。

      func intSum(x int, y int) int {
      	return x + y
      }
      
      func main() {
      	ret := intSum(10, 20)
      	fmt.Println(ret)
      }
      
      

    函数基础:参数

    • GO语言中没有默认参数!!

    类型简写

    • 如果定义函数时,把返回值也声明了,那么在函数体中无需再次声明

      func intSum(x int, y int) (ret int) {	// 也可以直接定义返回值名称和类型
      	ret = x + y		// 此时则不需要声明,应为定义函数时已经声明好返回值了
      	return 			// 返回值也可以省略,函数会自动寻找定义好的返回值名称
      }
      
      func main() {
      	ret := intSum(10, 20)
      	fmt.Println(ret)
      }
      

    可变参数

    • 可变参数是指函数的参数数量不固定。Go语言中的可变参数通过在参数名后加...来标识。

    • 本质上,函数的可变参数是通过切片来实现的。

    • 注意:可变参数通常作为函数的最后一个参数出现。

      // 函数接收可变参数,在参数名后面加... 表示可变参数
      // 可变参数在函数体中是切片类型
      func intSum(a ...int) int {
      	ret := 0
      	for _, arg := range a {
      		ret += arg
      	}
      	return	ret
      }
      func main() {
      	ret1 := intSum()
      	ret2 := intSum(10)
      	ret3 := intSum(10,20)
      	fmt.Println(ret1,ret2,ret3)
      }
      // 0 10 30
      
    • 固定参数和可变参数搭配使用

    • 多个参数如果类型一致,则只需要在参数末尾写类型即可,参数之间用逗号隔开

      // 固定参数和可变参数同时出现时,可变参数要放在最后
      func intSum(a, b ...int) int {
      	ret := a
      	for _, arg := range b {
      		ret += arg
      	}
      	return	ret
      }
      
      func main() {
      	ret1 := intSum(100)
      	ret2 := intSum(100,200,300)
      	fmt.Println(ret1,ret2)
      }
      // 100 600
      

    函数基础:返回值

    多返回值

    • 定义多个返回值,函数如果有多个返回值时必须用()将所有返回值包裹起来。

      // 定义具有多个返回值的函数
      // 多返回值也支持类型简写
      func calc(a, b int) (sum, sub int) {
      	sum = a + b
      	sub = a - b
      	return
      }
      
      func main() {
      	// 函数调用
      	x, y := calc(200, 100)
      	fmt.Println(x,y)
      }
      

    返回值补充

    • 当我们的一个函数返回值类型为slice时,nil可以看做是一个有效的slice,没必要显示返回一个长度为0的切片。

      func someFunc(x string) []int {
      	if x == "" {
      		return nil // 没必要返回[]int{}
      	}
      	...
      }
      

    函数高阶:作用域

    全局变量

    • 全局变量是定义在函数外部的变量,它在程序整个运行周期内都有效。 在函数中可以访问到全局变量。

    局部变量

    • 函数内定义的变量无法在函数外使用
    • 如果局部变量和全局变量重名,优先访问局部变量。
    • 语句块定义的变量:在if条件判断、for循环、switch语句上定义的变量,只能在其中使用

    项目作用域

    • 在一个项目中不同的包下,使用首字母大写的变量、函数、slice、map等都在该项目的其他文件中调用

      // tt包中创建:
      package tt
      import "fmt"
      
      // 私有的全局变量
      var a int = 100
      // 项目中所有都可以调用的全局变量
      var A int = 18
      
      func f1(){
      	fmt.Println("f1")
      }
      
      func F1()  {
      	fmt.Println("F1")
      }
      
      // main包中使用:
      package main
      
      import (
      	"fmt"
      	"github.com/liyn/day02/tt"
      )
      
      func main() {
      	tt.A = 200	// 调用tt包中的A变量
      	tt.F1()		// 调用tt包中的F1函数
      	fmt.Println(tt.A)
      }
      // F1
      // 200
      

    函数高阶:函数作为变量

    • 函数可以作为变量的值

      func add(x,y int)int{
      	return x+y
      }
      func main(){
      	ret := add
      	fmt.Println(ret(5,6))
      }
      // 11
      

    函数高阶:函数作为参数

    • 函数可以作为参数

      func add(x,y int)int{
      	return x+y
      }
      
      func sub(x,y int)int{
      	return x - y
      }
      
      func calc(x,y int,op func(a,b int)int)int {
      	return op(x,y)
      }
      
      func main() {
      	r1 := calc(200,100,add)
      	r2 := calc(200,100,sub)
      	fmt.Println(r1,r2)
      }
      

    函数高阶:函数作为返回值

    • 函数也可以作为返回值

      // 定义一个函数它的返回值是一个函数
      func testDemo() func(){
      	return func() {
      		fmt.Println("沙河小王子")
      	}
      }
      func main(){
      	ret := testDemo()
      	ret()	// 相当于执行了testDemo函数内部的匿名函数
      }
      

    函数高阶:匿名函数

    • 匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数:

      • 方式一:将匿名函数保存到变量中,执行变量()就可以执行匿名函数了
      • 方式二:匿名函数末尾加参数,可直接执行匿名函数
      // 格式:
      func(参数)(返回值){
          函数体
      }
      
      // 示例:
      func main() {
      	// 将匿名函数保存到变量
      	add := func(x, y int) {
      		fmt.Println(x + y)
      	}
      	add(10, 20) // 通过变量调用匿名函数
      
      	//自执行函数:匿名函数定义完加()直接执行
      	func(x, y int) {
      		fmt.Println(x + y)
      	}(10, 20)
      }
      

    函数高阶:闭包

    • 闭包内部的变量不会回收

    • 无参数的闭包

      // 定义一个函数它的返回值是一个函数
      func testDemo() func(){
      	name := "沙河娜扎"
      	return func() {
      		fmt.Println("hello", name)
      	}
      }
      func main(){
      	// 闭包 = 函数 + 外层变量的引用
      	ret := testDemo()	// ret此时就是一个闭包
      	ret()	// 相当于执行了testDemo函数内部的匿名函数
      }
      
    • 闭包示例一:

      // 定义一个函数它的返回值是一个函数
      func testDemo(name string) func(){
      	return func() {
      		fmt.Println("hello", name)
      	}
      }
      
      func main(){
      	ret := testDemo("沙河娜扎")
      	ret()
      }
      
    • 闭包示例二:

      // 判断一个字符串是否以指定字段结尾:
      
      // 定义一个函数它的返回值是一个函数
      func makeSuffixFunc(suffix string) func(string)string{
      	return func(name string) string {
      		if !strings.HasSuffix(name,suffix){ // 检测是否以第二个参数为末尾的值
      			return name + suffix
      		}else {
      			return name
      		}
      	}
      }
      func main() {
          // 闭包 = 函数 + 外层变量的引用
      	r := makeSuffixFunc(".txt")
      	ret := r("沙河娜扎")
      	fmt.Println(ret)
      }
      // 沙河娜扎.txt
      
    • 闭包示例三:

      // 定义一个函数它的返回值是一个函数
      func calc(base int) (func(int) int, func(int) int) {
      	add := func(i int) int {
      		base += i
      		return base
      	}
      
      	sub := func(i int) int {
      		base -= i
      		return base
      	}
      	return add, sub
      }
      func main() {
          // base一直没有消
      	x,y := calc(100)
      	ret1 := x(200)
      	fmt.Println(ret1) // 此时base=100,add函数参数i=200,结果为300
      	ret2 := y(300)
      	fmt.Println(ret2) // 此时base=300,sub函数参数i=300,结果为0
      }
      

    defer语句

    • defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。

    • 由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、加锁解锁及记录时间等。

        func main() {
        	fmt.Println("start")
        	defer fmt.Println(1)
        	defer fmt.Println(2)
        	defer fmt.Println(3)
        	fmt.Println("end")
        }
        // start
        // end
        // 3
        // 2
        // 1
      
    • defer不管出不出异常,肯定执行,为了关闭资源

      func chuF(a,b int)int{
      	return a / b
      }
      func main() {
      	defer fmt.Println("数据库关闭")
      	defer chuF(3,0)
      	defer fmt.Println("数据库开启")
      }
      // 数据库开启
      // 数据库关闭
      // panic: runtime error: integer divide by zero
      
    • 如果defer中有函数,就先执行函数

      func calc(index string, a, b int) int {
      	ret := a + b
      	fmt.Println(index, a, b, ret)
      	return ret
      }
      
      func main() {
      	x := 1
      	y := 2
      	defer calc("AA", x, calc("A", x, y))	 // 先执行calc中的函数
      	x = 10
      	defer calc("BB", x, calc("B", x, y))
      	y = 20
      }
      /*
      	A 1 2 3
      	B 10 2 12
      	BB 10 12 22
      	AA 1 3 4
      */
      

    defer执行时机

    • 在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。

    内置函数介绍

    内置函数 介绍
    close 主要用来关闭channel
    len 用来求长度,比如string、array、slice、map、channel
    new 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针
    make 用来分配内存,主要用来分配引用类型,比如chan、map、slice
    append 用来追加元素到数组、slice中
    panic和recover 用来做错误处理
  • 相关阅读:
    递归回溯 UVa140 Bandwidth宽带
    嵌入式设备网络设置
    海思板卡SATA最佳读写块大小测试
    Linux日志轮循实现(shell)
    检测应用的内存泄漏情况(shell)
    Python的交叉编译移植至arm板
    写一个简单的配置文件和日志管理(shell)
    shell 实现主板测试
    主板硬件性能测试
    C语言 模2除法
  • 原文地址:https://www.cnblogs.com/lynlearnde/p/13802406.html
Copyright © 2011-2022 走看看