Go语言之函数补充defer语句,递归函数,章节练习

defer语句是go语言提供的一种用于注册延迟调用的机制,是go语言中一种很有用的特性。

defer语句注册了一个函数调用,这个调用会延迟到defer语句所在的函数执行完毕后执行,所谓执行完毕是指该函数执行了return语句、函数体已执行完最后一条语句或函数所在协程发生了恐慌。

fmt.Println("test01")
defer fmt.Println("test02")
fmt.Println("test03")

编程经常会需要申请一些资源,比如数据库连接、打开文件句柄、申请锁、获取可用网络连接、申请内存空间等,这些资源都有一个共同点那就是在我们使用完之后都需要将其释放掉,否则会造成内存泄漏或死锁等其它问题。但操作完资源忘记关闭释放是正常的,而defer可以很好解决这个问题

// 打开文件
file_obj,err:=os.Open("满江红")
if err != nil {
    fmt.Println("文件打开失败,错误原因:",err)
}
// 关闭文件
defer file_obj.Close()
// 操作文件

多个defer执行顺序

当一个函数中有多个defer语句时,会按defer定义的顺序逆序执行,也就是说最先注册的defer函数调用最后执行。

fmt.Println("test01")
defer fmt.Println("test02")
fmt.Println("test03")
defer fmt.Println("test04")
fmt.Println("test05")

defer的拷贝机制

// 案例1
foo := func() {
fmt.Println("I am function foo1")
}
defer foo()
foo = func() {
fmt.Println("I am function foo2")
}

// 案例2
x := 10
defer func(a int) {
fmt.Println(a)
}(x)    
x++

// 案例3
x := 10
defer func() {
    fmt.Println(x)   // 保留x的地址
}()
x++

当执行defer语句时,函数调用不会马上发生,会先把defer注册的函数及变量拷贝到defer栈中保存,直到函数return前才执行defer中的函数调用。需要格外注意的是,这一拷贝拷贝的是那一刻函数的值和参数的值。注册之后再修改函数值或参数值时,不会生效。

defer执行时机

在Go语言的函数 return 语句不是原子操作,而是被拆成了两步

rval = xxx
ret

而 defer 语句就是在这两条语句之间执行,也就是

rval = xxx
defer_func
ret rval


defer x = 100
x := 10
return x  // rval=10.   x = 100, ret rval

经典面试题:

package main

import "fmt"

func f1() int {
    i := 5
    defer func() {
        i++
    }()
    return i
}
func f2() *int {

    i := 5
    defer func() {
        i++
        fmt.Printf(":::%p\n", &i)
    }()
    fmt.Printf(":::%p\n", &i)
    return &i
}

func f3() (result int) {
    defer func() {
        result++
    }()
    return 5 // result = 5;ret result(result替换了rval)
}

func f4() (result int) {
    defer func() {
        result++
    }()
    return result // ret result变量的值
}

func f5() (r int) {
    t := 5
    defer func() {
        t = t + 1
    }()
    return t // ret r = 5 (拷贝t的值5赋值给r)
}

func f6() (r int) {
    fmt.Println(&r)
    defer func(r int) {
        r = r + 1
        fmt.Println(&r)
    }(r)
    return 5
}

func f7() (r int) {
    defer func(x int) {
        r = x + 1
    }(r)
    return 5
}

func main() {

    // println(f1())
    // println(*f2())
    // println(f3())
    // println(f4())
    // println(f5())
    // println(f6())
    // println(f7())

}

在命名返回方式中,最终函数返回的就是命名返回变量的值,因此,对该命名返回变量的修改会影响到最终的函数返回值!

递归函数

一种计算过程,如果其中每一步都要用到前一步或前几步的结果,称为递归的。用递归过程定义的函数,称为递归函数,例如连加、连乘及阶乘等。

递归特性:

调用自身函数
必须有一个明确的结束条件
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,
每当进入一个函数调用,栈就会加一层栈帧,每当函数返 回,栈就会减一层栈帧。
由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
package main

import "fmt"

func factorial(n int)int{
    if n == 0{
        return 1
    }
    return n * factorial(n-1)

}

func main() {

    // 计算n的阶乘,即 n!
    var ret = factorial(4)
    fmt.Println(ret)
}

在这里插入图片描述
这个数列生成规则很简单,每一项都是前两项的和,举例 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……

package main

import "fmt"

func fib(n int) int {
    if n == 2 || n == 1 {
        return 1
    }
    return fib(n-1) + fib(n-2)

}

func main() {

    // 计算n的阶乘,即 n!
    ret:=fib(6)
    fmt.Println(ret)
}

在这里插入图片描述

练习题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

// 构建数据存储结构
var customers []map[string]interface{}
var customersId int

func findById(id int) int {
    index := -1
    //遍历this.customers切⽚
    for i := 0; i < len(customers); i++ {

        if customers[i]["cid"] == id {
            index = i
        }

    }
    return index
}

func isBack() bool {
    // 引导用户选择继续还是返回
    fmt.Print("请问是否返回上一层【Y/N】:")
    var backChoice string
    fmt.Scan(&backChoice)
    if strings.ToUpper(backChoice) == "Y" {
        return true
    } else {
        return false
    }
}

func inputInfo() (string, string, int8, string) {
    var name string
    fmt.Print("请输入客户姓名:")
    fmt.Scan(&name)

    var gender string
    fmt.Print("请输入客户性别:")
    fmt.Scan(&gender)

    var age int8
    fmt.Print("请输入客户年龄:")
    fmt.Scan(&age)

    var email string
    fmt.Print("请输入客户邮箱:")
    fmt.Scan(&email)

    return name, gender, age, email

}

func addCustomer() {
    for true {
        // 引导用户输入学号和姓名
        fmt.Printf("\033[1;35;40m%s\033[0m\n", "---------------------------添加客户开始-----------------------------")
        name, gender, age, email := inputInfo()
        // 创建客户的map对象
        customersId++ // 客户编号不需要输入,系统自增即可
        newCustomer := map[string]interface{}{
            "cid":    customersId,
            "name":   name,
            "gender": gender,
            "age":    age,
            "email":  email,
        }
        // 添加客户map对象添加到客户切片中
        customers = append(customers, newCustomer)
        fmt.Printf("\033[1;35;40m%s\033[0m\n", "---------------------------添加客户完成-----------------------------")
        b := isBack()
        if b {
            break
        }
    }
}

func listCustomer() {
    for true {
        fmt.Printf("\033[1;32;40m%s\033[0m\n", "----------------------------------客户列表开始-----------------------------------")
        for _, customer := range customers {
            fmt.Printf("编号:%-8d 姓名:%-8s 性别:%-8s 年龄:%-8d 邮箱:%-8s \n",
                customer["cid"], customer["name"], customer["gender"], customer["age"], customer["email"])
        }
        fmt.Printf("\033[1;32;40m%s\033[0m\n", "----------------------------------客户列表完成-----------------------------------")
        b := isBack()
        if b {
            break
        }
    }
}
func updateCustomer() {
    fmt.Printf("\033[1;36;40m%s\033[0m\n", "---------------------------客户修改开始----------------------------")
    for true {
        var updateCid int
        fmt.Print("请输入更新客户编号:")
        fmt.Scan(&updateCid)
        updateIndex := findById(updateCid)
        if updateIndex == -1 {
            fmt.Println("删除失败,输入的编号ID不存在")
            continue
        }
        fmt.Println("请输入修改客户的信息")
        name, gender, age, email := inputInfo()

        customers[updateIndex]["name"] = name
        customers[updateIndex]["gender"] = gender
        customers[updateIndex]["age"] = age
        customers[updateIndex]["email"] = email

        fmt.Printf("\033[1;36;40m%s\033[0m\n", "---------------------------客户修改完成----------------------------")
        b := isBack()
        if b {
            break
        }
    }
}

func deleteCustomer() {
    fmt.Printf("\033[1;31;40m%s\033[0m\n", "---------------------------删除客户开始----------------------------")
    var delCid int
    fmt.Print("请输入删除客户编号:")
    fmt.Scan(&delCid)

    delIndex := findById(delCid)
    if delIndex == -1 {
        fmt.Println("删除失败,输入的编号ID不存在")
        return
    }

    customers = append(customers[:delIndex], customers[delIndex+1:]...)
    fmt.Printf("\033[1;31;40m%s\033[0m\n", "---------------------------删除客户完成----------------------")

}

var data = make(map[string]map[string]string)

func main() {

    for true {
        fmt.Printf("\033[1;33;40m%s\033[0m\n", `
----------------客户信息管理系统--------------
   1、添加客户
   2、查看客户
   3、更新客户
   4、删除客户
   5、退出
-------------------------------------------
`)

        var choice int
        fmt.Printf("\033[1;38;40m%s\033[0m", "请输入选择【1-5】:")
        stdin := bufio.NewReader(os.Stdin)
        fmt.Fscan(stdin, &choice)

        switch choice {
        case 1:
            addCustomer()
        case 2:
            listCustomer()
        case 3:
            updateCustomer()
        case 4:
            deleteCustomer()
        default:
            fmt.Println("非法输入!")
            os.Exit(0)
        }
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/41466.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue使用echarts根据页面大小 echarts窗口自适应

1. 使用window.onresize var myChart echarts.init(document.getElementById(myChart)); window.onresize () > {myChart.resize() }优点&#xff1a; 可以根据窗口大小实现自适应 缺点&#xff1a; window.onresize是绑定到window上的&#xff0c;切换vue页面时监听依…

EXCEl——移除单元格中换行符

方法一&#xff1a;使用清除格式功能 步骤如下: 1.选中需要取消换行的单元格 2.在“开始"选项卡中找到"清除”功能&#xff0c;点击下拉菜单中的“清除格式" 3.这时单元格的换行就被取消了。 清除前效果图 清除后效果图 方法一&#xff1a;使用函数功能 步骤…

uniapp H5预览PDF文件

1&#xff0c;下载资源后hybrid文件存放在static静态文件里 (点击这里去下载文件) 2&#xff0c;pdf预览页面配置 <template><view style"width: 100vh;"><web-view :src"pdfUrl"></web-view></view> </template><…

多旋翼物流无人机节能轨迹规划(Python代码实现)

&#x1f4a5;1 概述 多旋翼物流无人机的节能轨迹规划是一项重要的技术&#xff0c;可以有效减少无人机的能量消耗&#xff0c;延长飞行时间&#xff0c;提高物流效率。下面是一些常见的节能轨迹规划方法&#xff1a; 最短路径规划&#xff1a;通过寻找起点和终点之间的最短路径…

性能测试学习阶段性总结

目录 1.前言 2.概念部分 2.1不同角度看软件性能 2.2关键词 2.3测试的方法 2.4应用领域 3.性能测试过程模型&#xff08;PTGM&#xff09; 2.1测试前期准备 2.2测试工具引入 2.3测试计划 2.4测试设计与开发 2.5测试执行和管理 2.6测试分析 总结&#xff1a; 1.前言…

visio 图片转换到 latex 中

调整图片大小 在Visio中&#xff0c;设计–>页面设置–>大小–>适应绘图&#xff0c;这样会自动去除多余空白&#xff0c;保留部分空白作为边界&#xff0c;无需使用Word。 2. 将新的Visio文件另存为pdf格式文件 3. latex 中插入pdf 格式图片

使用更少数据训练更好的alpaca

概述 该论文的研究背景是指令微调在大型语言模型中取得了重要的成果&#xff0c;但现有的训练数据质量问题导致模型性能下降。 过去的方法主要是使用低质量的数据进行指令微调&#xff0c;这些数据中存在错误或无关的回答&#xff0c;导致结果误导和训练成本增加。该论文的方…

安卓通过adb pull和adb push 手机与电脑之间传输文件

1.可以参考这篇文章 https://www.cnblogs.com/hhddcpp/p/4247923.html2.根据上面的文章&#xff0c;我做了如下修改 //设置/system为可读写&#xff1a; adb remount //复制手机中的文件到电脑中。需要在电脑中新建一个文件夹&#xff0c;我新建的文件夹为ce文件夹 adb pull …

【iOS】—— 属性关键字及weak关键字底层原理

文章目录 先来看看常用的属性关键字有哪些&#xff1a;内存管理有关的的关键字&#xff1a;&#xff08;weak&#xff0c;assign&#xff0c;strong&#xff0c;retain&#xff0c;copy&#xff09;关键字weak关键字assignweak 和 assign 的区别&#xff1a;关键字strong&#…

vue2的 element 表格单元格合并

<template><div><el-table show-summary :summary-method"getSummaries" :span-method"objectSpanMethod" :data"tableData" row-key"id" ref"tableDom" border><el-table-column label"序号&quo…

Windows与Linux取证分析

目录 一、电子数据取证基本概念 1.电子取证学 2.常规取证 3.洛卡德物质交换原理 4.电子数据范围 5.电子数据取证的概念和目的 6.电子数据取证过程 二、Linux系统取证 1.基本信息获取 &#xff08;1&#xff09;获取系统基础信息 &#xff08;2&#xff09;用户/用户…

Baichuan-13B:130亿参数的开源语言模型,引领中文和英文benchmark

Baichuan-13B: 一个强大的开源大规模语言模型 标题&#xff1a;Baichuan-13B&#xff1a;130亿参数的开源语言模型&#xff0c;引领中文和英文benchmark Baichuan-13B是由百川智能开发的一个开源大规模语言模型项目&#xff0c;包含了130亿参数。该模型在中文和英文的权威ben…

mongodb集群搭建

下载地址&#xff1a; https://www.mongodb.com/try/download/community下载mongodb-linux-x86_64-rhel70-5.0.18 搭建集群 tar -zxvf mongodb-linux-x86_64-rhel70-5.0.18.tgz mkdir -p data/dp cd mongodb-linux-x86_64-rhel70-5.0.18 mkdir -p data/db mkdir log mkdir c…

MiniGPT4系列之二推理篇命令行方式:在RTX-3090 Ubuntu服务器推理详解

MiniGPT4系列之一部署篇&#xff1a;在RTX-3090 Ubuntu服务器部署步骤详解_seaside2003的博客-CSDN博客 MiniGPT4系列之二推理篇命令行方式&#xff1a;在RTX-3090 Ubuntu服务器推理详解_seaside2003的博客-CSDN博客 MiniGPT4系列之三模型推理 (Web UI)&#xff1a;在RTX-309…

pytorch 2.0初探:和pytorch 1.13的速度对比

看到pytorch2.0出来了&#xff0c;而且宣传提速明显&#xff0c;一行代码即可提速43%左右&#xff1a; compiled_model torch.compile(model) We then measure speedups and validate accuracy across these models. Since speedups can be dependent on data-type, we measu…

Ubuntu学习笔记(二)——文件属性与权限

文章目录 前言一、用户与用户组1.用户&#xff08;文件拥有者&#xff09;2.用户组3.其他人 二、Linux用户身份与用户组记录文件1. /etc/passwd2. /etc/shadow3. /etc/group 三、文件属性与权限1. 查看文件属性的方法&#xff08;ls&#xff09;2.文件属性详细介绍2.1 权限2.2 …

【Redis】高可用之三:集群(cluster)

本文是Redis系列第6篇&#xff0c;前5篇欢迎移步 【Redis】不卡壳的 Redis 学习之路&#xff1a;从十大数据类型开始入手_AQin1012的博客-CSDN博客关于Redis的数据类型&#xff0c;各个文章总有些小不同&#xff0c;我们这里讨论的是Redis 7.0&#xff0c;为确保准确&#xf…

Spring Boot进阶(55):SpringBoot之集成MongoDB及实战使用 | 超级详细,建议收藏

1. 前言&#x1f525; 前几期我们有介绍Mysql、Redis等数据库介绍及实战演示&#xff0c;对基本的数据存放有很好的共性&#xff0c;但是如果说遇到大面积的xml、Json、bson等格式文档数据存放&#xff0c;以上数据库并非是最优选择&#xff0c;最优选择是Mongodb数据库。 那么…

采集极验4滑块验证码图片数据

在网络安全领域&#xff0c;验证码是一种常见的用于验证用户身份或防止恶意机器人攻击的技术。而极验4滑块验证码作为一种广泛应用的验证码形式&#xff0c;其具有较高的安全性和防御能力。本文将以获取极验4滑块验证码图片数据为主题&#xff0c;介绍相关技术和方法。 一、极…

【测试设计】使用jenkins 插件Allure生成自动化测试报告

前言 以前做自动化测试的时候一直用的HTMLTestRunner来生成测试报告&#xff0c;后来也尝试过用Python的PyH模块自己构建测试报告&#xff0c;在后来看到了RobotFramework的测试报告&#xff0c;感觉之前用的测试报告都太简陋&#xff0c;它才是测试报告应该有的样子。也就是在…