日常学习总结golang(day2)---函数、闭包、defer

2024-03-12 17:40

本文主要是介绍日常学习总结golang(day2)---函数、闭包、defer,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.map:

Go语言中,提供映射关系容器的为map,其内部结构使用hash实现,引用类型必须初始化才能使用

2.函数:
  1. 函数是组织好的,可重复使用的,用于执行指定任务的代码块。

  2. 函数存在的意义:
    一段代码的封装;
    把一段逻辑抽象出来封装到一个函数中,给它起一个名字,每次用到它的时候直接调用就可以啦;
    使代码结构更清晰,更简介。

  3. 变量作用域
    (1) 全局变量
    (2)函数作用域
    i.先在函数内部找变量,找不到就往外层找;
    ii.函数内部的变量,外部是访问不到的。
    (3)代码块作用域(比如if,for里面定义的变量,外面无法访问)

  4. 函数中查找变量的顺序如下:
    (1)现在函数内部查找;
    (2)如果找不到就往函数外部查找,一直到全局.

  5. 高阶函数
    函数也是一种类型,它可以作为参数,也可以作为返回值。

    //函数可以作为参数的类型
    func f1(x func() int) {ret := x()fmt.Println(ret)//结果:3
    }
    func main() {f1(func() int {return 3})
    }
    
  6. 匿名函数
    函数内部没有办法声明带名字的函数,所以匿名函数一般用在函数内部。如果只是调用一次的函数,还可以简写成立即执行函数。

    func main() {//匿名函数:f1:= func(x,y int) {fmt.Println(x+y)//30}f1(10,20)
    }
    
    func main() {//立即执行函数func(x,y int) {fmt.Println(x+y)//300}(100,200)
    }
    
  7. 内置函数

内置函数介绍
close主要用来关闭channel
len用来求长度,比如:string、array、slice、map、channel
new用来分配内存,主要用来分配值类型,比如:int、string。返回的是指针。
make用来分配内存,主要用来分配引用类型,比如:slice、map、channel。
append用来追加元素到slice
panic和recover用来做错误处理
3.闭包:
  1. 闭包是什么?
    闭包是一个函数,这个函数包含了它外部作用域的一个变量。

  2. 底层的原理:
    (1)函数可以作为返回值;
    (2)函数内部查找变量的顺序,先在自己内部找,找不到往外层找。

    //闭包
    func add(x int) func(int) int {return func(y int) int {x += yreturn x}
    }
    func main() {ret := add(100)ret2:=ret(200)fmt.Println(ret2)//300
    }
    
  3. //闭包
    func calc(base int)(func(int)int,func(int) int) {add := func(i int) int {base += ireturn base}sub := func(i int) int {base -= ireturn base}return add,sub
    }
    func main() {f1,f2:=calc(10)//需要注意的是:自始自终都是一个base.fmt.Println(f1(1),f2(2))//f(1)返回11,这时base已经发生改变为11,在调用sub,此时base为11-2,返回9.fmt.Println(f1(3),f2(4))//同理,上面f(2)返回9,此时base=9,执行add,9+3=12.fmt.Println(f1(5),f2(6))
    }
    

    11 9
    12 8
    13 7

  4. 闭包解决两个带不同类型参数的函数合并问题。例子如下:

    //闭包
    func f1(f func()) {fmt.Println("this is f1")f()
    }
    func f2(x, y int) {fmt.Println("this is f2")fmt.Println(x + y)
    }//要求:f1(f2):因为f1中带的参数是无参函数类型,而f2是带有两个参数的,所以不能直接将f2传给f1.
    func f3(f func(int, int), x, y int) func() {return func() {f(x, y)}
    }
    func main() {ret := f3(f2, 10, 20)//把原来需要传递两个int类型的参数包装成一个不需要传参的函数(f2)f1(ret)//f3是返回无参函数类型,正好可以传给f1
    }
    

    this is f1
    this is f2
    30

4. defer:
  1. Go语言中defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行。

  2. defer 多用于函数结束之前,释放资源。(资源句柄、数据库连接、socket连接等)

  3. defer执行时机:
    在Go语言的函数中return语句在底层并不是原子操作,它会分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作之后,RET指令之前。具体如下图所示:
    在这里插入图片描述

    1. defer的经典案例
    func f1() int {x := 5defer func() {x++ //修改的是x,不是返回值}()return x //返回值=x=5
    }
    func f2() (x int) {defer func() {x++}()return 5 //1.返回值=x
    }
    func f3() (y int) {x := 5defer func() {x++ //修改的是x}()return 5 //1.返回值=y=x=5
    }
    func f4() (x int) {defer func(x int) {x++ //改变的是函数中的副本}(x)return 5 //返回值=x=5
    }
    func f5() (x int) {defer func(x int) int {x++return x //没有变量接收返回的x,相当于丢弃了}(x)return 5
    }//传一个x的指针到匿名函数中
    func f6() (x int) {defer func(x *int) {*x++ //修改直接修改了x的值}(&x)    //将x的内存地址传进去了,return 5 //返回值=x=5
    }
    func main() {fmt.Println(f1()) //5fmt.Println(f2()) //6fmt.Println(f3()) //5fmt.Println(f4()) //5fmt.Println(f5()) //5fmt.Println(f6()) //6
    }
    

    defer面试题:

    func calc(index string, a, b int) int {ret := a + bfmt.Println(index, a, b, ret)return ret
    }
    func main() {a := 1b := 2defer calc("1", a, calc("10", a, b))a = 0defer calc("2", a, calc("20", a, b))b = 1
    }
    

    解题思路:
    defer延迟调用了一个函数,这个函数里面的参数又有一个函数,这时候会先执行里面的函数,把执行结果确切的算出来.而不是最后再计算。
    1.a=1,b=2
    2.defer calc(“1”, 1, calc(“10”, 1, 2))
    3.calc(“10”, 1, 2) //1. (10,1,2,3)
    4.defer calc(“1”, 1,3) //4.(1,1,3,4)
    5.a=0
    6.defer calc(“2”, 0, calc(“20”, 0, 2))
    7.calc(“20”, 0, 2) //2. (20,0,2,2)
    8.defer calc(“2”, 0,2) //3.(2,0,2,2)
    9.b=1 //混淆。没有函数要执行它了

    执行结果:
    10 1 2 3
    20 0 2 2
    2 0 2 2
    1 1 3 4

5.panic和recover

Go语言中使用panic/recover模式处理错误,panic可在任何地方引发,但recover只有在defer调用的函数中有效。
注意:

  1. recover()必须搭配defer使用
  2. defer一定要在可能引发panic的语句之前定义
6. 练习题:

你有50枚金币,需要分配给以下几个人:Matthew、Sarah、Augustus、Heidi、Emilie、Peter、Giana、Adriano、Aaron、Elizabeth。
分配规则如下:
a. 名字中每包含1个’e’或’E’分1枚金币;
b.名字中每包含1个’i’或’I’分2枚金币;
c. 名字中每包含1个’o’或’O’分3枚金币;
d. 名字中每包含1个’u’或’U’分4枚金币;
写一个程序,计算每个用户分到多少金币,以及最后剩下多少金币?

package mainimport ("fmt"
)var (coins = 50users = []string{"Matthew", "Sarah", "Augustus", "Heidi", "Emilie","Peter", "Giana", "Adriano", "Aaron", "Elizabeth"}distribution = make(map[string]int, len(users))
)func dispatchCoin() (left int) {//1.依次拿到每个人的名字for _,name:=range users{//2.拿到一个人名根据分金币的规则分金币for _,c:=range name{switch c {case 'e','E'://满足这个条件分1枚金币distribution[name]++coins--case 'i','I'://满足这个条件分2枚金币distribution[name]+=2coins-=2case 'o','O'://满足这个条件分3枚金币distribution[name]+=3coins-=3case 'u','U'://满足这个条件分4枚金币distribution[name]+=4coins-=4}}}left=coinsreturn
}func main() {left := dispatchCoin()fmt.Println("剩下的金币数:",left)for k,v:=range distribution{fmt.Printf("%s:%d\n",k,v)}
}

执行结果:
剩下的金币数: 10
Emilie:6
Aaron:3
Giana:2
Adriano:5
Elizabeth:4
Matthew:1
Augustus:12
Heidi:5
Peter:2

这篇关于日常学习总结golang(day2)---函数、闭包、defer的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/802061

相关文章

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

golang 日志log与logrus示例详解

《golang日志log与logrus示例详解》log是Go语言标准库中一个简单的日志库,本文给大家介绍golang日志log与logrus示例详解,感兴趣的朋友一起看看吧... 目录一、Go 标准库 log 详解1. 功能特点2. 常用函数3. 示例代码4. 优势和局限二、第三方库 logrus 详解1.

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

kotlin的函数forEach示例详解

《kotlin的函数forEach示例详解》在Kotlin中,forEach是一个高阶函数,用于遍历集合中的每个元素并对其执行指定的操作,它的核心特点是简洁、函数式,适用于需要遍历集合且无需返回值的场... 目录一、基本用法1️⃣ 遍历集合2️⃣ 遍历数组3️⃣ 遍历 Map二、与 for 循环的区别三、高

C语言字符函数和字符串函数示例详解

《C语言字符函数和字符串函数示例详解》本文详细介绍了C语言中字符分类函数、字符转换函数及字符串操作函数的使用方法,并通过示例代码展示了如何实现这些功能,通过这些内容,读者可以深入理解并掌握C语言中的字... 目录一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现3.1strlen函数3.2st

Java反转字符串的五种方法总结

《Java反转字符串的五种方法总结》:本文主要介绍五种在Java中反转字符串的方法,包括使用StringBuilder的reverse()方法、字符数组、自定义StringBuilder方法、直接... 目录前言方法一:使用StringBuilder的reverse()方法方法二:使用字符数组方法三:使用自

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx