字节青训翻译项目 + Golang使用笔记

基础语法

go 语言的开始 肯定少不了 Hello World

package main
import (
    "fmt"
)
func main() {
    fmt.Println("hello worl")
}

下划线

“_”是特殊标识符,用来忽略结果。

golang 语言一般拥有多返回值 使用一个变量接收结果,一个接收错误 或者 使用 _ 用来忽略错误

go中 的:==

输入输出

fmt 标准库的输入输出

输入

  • fmt.Scan() 变量类型输入

  • fmt.Scanf() 按照指定格式获取输入

  • fmt.Scanln() 获取一行的输入 只有一行

输出

  • fmt.Print() 按照变量进行输出

  • fmt.Println() 输出并且换行

  • fmt.Printf() 安装格式指定输出

其中输出的时候 %b| %v| %+v | %#v+|

%b:这个占位符用于输出一个整数的二进制表示。

%v:这是一个通用的占位符,用于输出值的默认格式。

%+v:这个占位符与 %v 类似,但它在输出结构体时会添加字段名。

%#v:这个占位符用于输出值的Go语法表示,包括结构体的名称和字段。

%T: 输出 变量类型

TestCode

package main
​
import (
    "fmt"
)
type Mystruct struct {
    age  int
    name string
}
func main() {
​
    var cc = [6]Mystruct{}
    fmt.Scanf("%d %s", &cc[0].age, &cc[0].name)
    fmt.Printf("binary : %b : %d %s \n", cc[0].age, cc[0].age, cc[0].name)
    fmt.Printf("-------------------------------------\n")
    fmt.Scan(&cc[1].age, &cc[1].name)
    fmt.Print(cc[1])
    fmt.Printf("-------------------------------------\n")
    fmt.Scan(&cc[2].age)
    fmt.Scanln(&cc[2].name)
    println("--------------------------")
    fmt.Println(cc[2].age, cc[2].name)
    fmt.Printf("-------------------------------------\n")
    fmt.Printf("%v\n%+v\n%#v\n%+#v\n%T\n", cc, cc, cc, cc, cc)
}
​

PS D:\C zhuomian\goProg\mygodemo> go run main.go 11 ondeone

binary : 1011 : 11 ondeone

22 twotwo {22 twotwo}-------------------------------------

33

33

[{11 ondeone} {22 twotwo} {33 } {0 } {0 } {0 }] [{age:11 name:ondeone} {age:22 name:twotwo} {age:33 name:} {age:0 name:} {age:0 name:} {age:0 name:}] [6]main.Mystruct{main.Mystruct{age:11, name:"ondeone"}, main.Mystruct{age:22, name:"twotwo"}, main.Mystruct{age:33, name:""}, main.Mystruct{age:0, name:""}, main.Mystruct{age:0, name:""}, main.Mystruct{age:0, name:""}} [6]main.Mystruct{main.Mystruct{age:11, name:"ondeone"}, main.Mystruct{age:22, name:"twotwo"}, main.Mystruct{age:33, name:""}, main.Mystruct{age:0, name:""}, main.Mystruct{age:0, name:""}, main.Mystruct{age:0, name:""}} [6]main.Mystruct

手动实现mygoguess-game.go

package main
import (
    "fmt"
    "math/rand"
    "time"
)
func main() {
    MAXnum := 19999
    rand.Seed(time.Now().UnixNano()) //seed 随机种子
    randd := rand.Intn(MAXnum)       // 随机数
    var innum int
​
    fmt.Println("请输入一个数字")
    for {
        fmt.Scanln(&innum)
        if innum == randd {
            fmt.Println("恭喜你猜对了")
            break
        }
        if innum > randd {
            fmt.Println("你猜的数字大了")
        }
        if innum < randd {
            fmt.Println("你猜的数字小了")
        }
    }
}

Sprint 系列函数

  • fmt.Sprint, fmt.Sprintf, fmt.Sprintln 等函数都属于 Sprint 系列。

  • 功能:这些函数会根据传入的参数生成一个字符串并返回。

    • fmt.Sprint(args...):直接将参数拼接成字符串。

    • fmt.Sprintf(format, args...):根据 format 的格式生成字符串(类似于 C 中的 printf)。

    • fmt.Sprintln(args...):会在参数之间加入空格,并在结尾加上换行符。

Errorf 函数

  • 功能fmt.Errorf 根据格式化参数生成一个错误类型 error

    err := fmt.Errorf("an error occurred: %d", errorCode)
  • 用法:适用于生成带有格式化信息的错误,便于调试。

Sscan 系列函数

这些函数从字符串中扫描数据并解析到变量中:

Sscan

  • 功能:从字符串中依次读取数据,将其解析为传入的变量。

  • 用法

    var name string
    var age int
    fmt.Sscan("Alice 30", &name, &age) // name = "Alice", age = 30

Sscanln

  • 功能:与 Sscan 类似,但在遇到换行符时会停止扫描。

  • 用法

    var name string
    var age int
    fmt.Sscanln("Alice 30\n", &name, &age) // name = "Alice", age = 30

Sscanf`

  • 功能:从字符串中按指定格式读取数据。

  • 用法

    var name string
    var age int
    fmt.Sscanf("Name: Alice Age: 30", "Name: %s Age: %d", &name, &age) // name = "Alice", age = 30

Fscan 系列函数

这些函数用于从 io.Reader 类型中读取数据。常见的 io.Reader 类型包括文件、标准输入等。

Fscan

  • 功能:从 Reader 中逐行读取数据并解析到变量。

  • 用法

    var name string
    var age int
    fmt.Fscan(reader, &name, &age)

Fscanln

  • 功能:与 Fscan 类似,但遇到换行符时停止扫描。

  • 用法

    var name string
    var age int
    fmt.Fscanln(reader, &name, &age)

Fscanf

  • 功能:按指定格式从 Reader 中读取数据。

  • 用法

    var name string
    var age int
    fmt.Fscanf(reader, "Name: %s Age: %d", &name, &age)

bufio.NewReaderbufio

bufio 是 Go 标准库中的缓冲读取库。bufio.NewReader 可以创建一个带缓冲的读取器,用于更高效地从输入源读取数据。

  • bufio.NewReader 用法:

    reader := bufio.NewReader(os.Stdin)
    • 创建一个基于标准输入(os.Stdin)的带缓冲读取器 reader

    • 使用 reader.ReadString('\n') 可以读取一行输入,直到遇到换行符。

  • bufio 库的优势:

    • 使用缓冲提高读取效率:bufio.Reader 会在内存中维护一个缓冲区,减少底层 I/O 操作次数,提高读取性能。

    • 提供了如 ReadLineReadString 等方便的方法,可以轻松读取多种数据格式。

bufio包

一、读取函数

bufio.Reader 提供的缓冲读取功能使得读取数据更高效且便捷。创建 Reader 可以通过 bufio.NewReader 来实现:

reader := bufio.NewReader(os.Stdin)

常用读取方法

ReadByte

  • 功能:读取并返回一个字节。

  • 用法

    b, err := reader.ReadByte()

ReadRune

  • 功能:读取一个 Unicode 字符(rune),返回该字符及其字节大小。

  • 用法:常用于读取 UTF-8 编码的字符。

    r, size, err := reader.ReadRune()

ReadString

  • 功能:读取直到指定的分隔符(如 '\n')并返回字符串。

  • 用法:适合按行读取用户输入。

    input, err := reader.ReadString('\n')

ReadLine`

  • 功能:按行读取数据,返回一行内容,不包含行尾的换行符。适合处理长行数据,因为可以分段读取。

  • 用法

    line, isPrefix, err := reader.ReadLine()
    • isPrefix:为 true 表示当前行未读完,需继续读取。

ReadBytes

  • 功能:读取到指定的字节(分隔符)并返回 []byte 数组。

  • 用法:适合需要读取特定分隔符时使用。

    data, err := reader.ReadBytes('\n')

Peek

  • 功能:从缓冲区中查看前 n 个字节,而不移动读取位置。适合检查接下来要处理的数据。

  • 用法

    peekBytes, err := reader.Peek(5)

Buffered

  • 功能:返回缓冲区中现有的未读取字节数。

  • 用法:可用于检查缓冲区中是否有足够数据。

    bufferedBytes := reader.Buffered()

二、写入函数

bufio.Writer 提供了缓冲写入功能,可以减少 I/O 操作次数,从而提高效率。创建 Writer 可以通过 bufio.NewWriter 来实现:

writer := bufio.NewWriter(os.Stdout)

常用写入方法

Write

  • 功能:直接将 []byte 写入缓冲区。

  • 用法

    writer.Write([]byte("Hello"))

WriteByte

  • 功能:将单个字节写入缓冲区。

  • 用法

    writer.WriteByte('H')

WriteRune

  • 功能:将 Unicode 字符写入缓冲区,适合写入 UTF-8 编码字符。

  • 用法

    writer.WriteRune('你')

WriteString

  • 功能:直接将字符串写入缓冲区。

  • 用法

    writer.WriteString("Hello, World!")

Flush

  • 功能:将缓冲区内容立即写入底层 io.Writer(如 os.Stdout)。

  • 用法:在完成一系列写入后需要 Flush,确保数据被实际写出。

    writer.Flush()

Buffered

  • 功能:返回缓冲区中尚未写入的字节数。

  • 用法:可用于检查缓冲区是否快满或为空。

    bufferedBytes := writer.Buffered()

Test

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)

	fmt.Print("请输入一行文本:")
	// 读取一行输入
	input, _ := reader.ReadString('\n')
	// 转换为大写
	input = strings.ToUpper(input)
	// 写入到输出缓冲区
	writer.WriteString("转换为大写后:" + input)
	// Flush 输出缓冲区内容
	writer.Flush()
}
  • bufio.Reader:提供多种读取方法,方便逐字节、逐行、指定长度等方式读取数据。

  • bufio.Writer:在缓冲区中收集数据,统一 Flush 输出,减少 I/O 操作频率。

go 翻译 小 demo

使用工具

Convert curl commands to Go

在线JSON转Golang Struct - JSON中文网

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
)

type JSONData []struct {
	Translations []struct {
		Text            string `json:"text"`
		Transliteration struct {
			Text   string `json:"text"`
			Script string `json:"script"`
		} `json:"transliteration"`
		To      string `json:"to"`
		SentLen struct {
			SrcSentLen   []int `json:"srcSentLen"`
			TransSentLen []int `json:"transSentLen"`
		} `json:"sentLen"`
	} `json:"translations"`
	DetectedLanguage struct {
		Language string `json:"language"`
	} `json:"detectedLanguage"`
}

func main() {

	if len(os.Args) != 2 {
		fmt.Print("Err < 2")
		os.Exit(1)
	}
	word := os.Args[1]
	client := &http.Client{}
	headl := "&fromLang=en&to=zh-Hans&token=cfdSz0lBcTJ0mYIx-idHdwW2WsB7BWeb&key=1730728315435&text="
	headr := "%0A&tryFetchingGenderDebiasedTranslations=true"

	var data = strings.NewReader(headl + word + headr)
	req, err := http.NewRequest("POST", "https://cn.bing.com/ttranslatev3?&IG=0D2DD352B09B429AA737FF0B91CF7E3A&IID=SERP.5611", data)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("accept", "*/*")
	req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en-US;q=0.8,en-GB;q=0.7,en;q=0.6")
	req.Header.Set("content-type", "application/x-www-form-urlencoded")
	req.Header.Set("cookie", "_EDGE_V=1; MUID=3DD806F5E6F96D3724B6122BE7BA6C66; MUIDB=3DD806F5E6F96D3724B6122BE7BA6C66; SRCHD=AF=NOFORM; SRCHUID=V=2&GUID=E28660BFF60D4F238469111E446AE1FC&dmnchg=1; ANON=A=E937F8F3346CF4C763B336C0FFFFFFFF; MMCASM=ID=DA0EBA777BC94EADAC77E8AADA144F7C; _UR=QS=0&TQS=0&Pn=0; MUIDB=3DD806F5E6F96D3724B6122BE7BA6C66; MSPTC=-b_60jJeBZwD8J8VGqLMX_hpgKYf7fDd5ex_VUTObB0; SnrOvr=X=rebateson; _HPVN=CS=eyJQbiI6eyJDbiI6NSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiUCJ9LCJTYyI6eyJDbiI6NSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiSCJ9LCJReiI6eyJDbiI6NSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiVCJ9LCJBcCI6dHJ1ZSwiTXV0ZSI6dHJ1ZSwiTGFkIjoiMjAyNC0xMS0wMlQwMDowMDowMFoiLCJJb3RkIjowLCJHd2IiOjAsIlRucyI6MCwiRGZ0IjpudWxsLCJNdnMiOjAsIkZsdCI6MCwiSW1wIjoxMSwiVG9ibiI6MH0=; _clck=1t8fudo%7C2%7Cfqj%7C1%7C1724; _uetvid=65612ea0822411efbded8727e2044d11; btstkn=Tl7pY6tCC3fvzW8SLomdC00avFy%252F9M7sEUzBBeUlccpjqL2Pmat1vCmKcD95kpaM3LkSENBW62CdRAOAKad81dzIw5Md9nhjqkbZ4R0Exkw%253D; WLS=C=c21e495c9a293e97&N=%e9%91%ab%e8%87%a3; _Rwho=u=d&ts=2024-11-04; CortanaAppUID=2664D2F3F925D133F3152F684CF76B6E; _EDGE_S=SID=38C6BACFBE90697D0A75AFE2BF986891&mkt=zh-CN&ui=zh-cn; ipv6=hit=1730717206641&t=4; _tarLang=default=zh-Hans; _TTSS_IN=hist=WyJ6aC1IYW5zIiwiZW4iLCJhdXRvLWRldGVjdCJd&isADRU=0; _TTSS_OUT=hist=WyJlbiIsInpoLUhhbnMiXQ==; SRCHUSR=DOB=20240817&T=1730721490000; ABDEF=V=13&ABDV=13&MRNB=1730721613137&MRB=0; _SS=SID=38C6BACFBE90697D0A75AFE2BF986891&R=2746&RB=2746&GB=0&RG=17915&RP=2746&PC=U531; GC=YnxFrylq8fwg8A7dhcQYV3GTNjCGYaOAsyyTi2egLrQWNaNwWTDvA38X8hkfihz8YH0TSCq3AHgfoE60Nd8m3w; SRCHHPGUSR=SRCHLANG=zh-Hans&PV=15.0.0&DM=0&BRW=NOTP&BRH=T&CW=804&CH=1202&SCW=1164&SCH=3115&DPR=1.3&UTC=480&EXLTT=32&HV=1730721638&PRVCW=804&PRVCH=1202&BZA=0&THEME=0&WEBTHEME=0&PR=1.25&IG=A29C8756EB9A4FB498E60F5F0D275D1D; _RwBf=r=1&mta=0&rc=2746&rb=2746&gb=0&rg=17915&pc=2746&mtu=0&rbb=0.0&g=0&cid=&clo=0&v=16&l=2024-11-04T08:00:00.0000000Z&lft=0001-01-01T00:00:00.0000000&aof=0&ard=0001-01-01T00:00:00.0000000&rwdbt=1730524633&rwflt=1730508731&o=16&p=MULTIGENREWARDSCMACQ202205&c=ML2357&t=3238&s=2022-12-05T12:33:52.9946010+00:00&ts=2024-11-04T12:00:36.7461101+00:00&rwred=0&wls=0&wlb=0&wle=0&ccp=2&cpt=0&lka=0&lkt=0&aad=0&TH=&e=_NWZuWI9JkZbPZYLV3BRPfM8SAZ-FNeWAsJADUHG_-kwyhHr4Ie8SfV3DWoDU3-7IYvYoVCIHD2HwMurudshQg&A=E937F8F3346CF4C763B336C0FFFFFFFF&rwaul2=0&rwmrst=2024-09-25T09:00:23+08:00; SNRHOP=I=&TS=; _U=1TBYMmfO53oLk8kA_5z2YZ8qulngPq15SyUZ8kHniyTE6nQnuJklbyUjUlJVw1NpXZTq55JZ-NF0PDIFqtO21-2M4xQADotvnQpqejAm-9p_W2gpL7fbr1C8zXuSHp96eDt44Tnsrv9IG36_phkVHdtjy99ckx0qdFyvAUGBwlHpJrsd1-WY7YExjYQwll3Sm1brlLVTG7mhMcrsxXoYbx_ngU-5zW5cL5J7HZkzEogA; TTRSL=en; USRLOC=HS=1&ELOC=LAT=36.65978240966797|LON=117.00499725341797|N=%E5%B8%82%E4%B8%AD%E5%8C%BA%EF%BC%8C%E5%B1%B1%E4%B8%9C%E7%9C%81|ELT=2|&CLOC=LAT=36.6597836352021|LON=117.0049957676959|A=733.4464586120832|TS=241104125942|SRC=W&BID=MjQxMTA0MjA1OTM4X2EzY2Y5OTFjMjU4NzJiNTg4YTNiNzg1MTc4MDdlNmM4NTUyNTg2YTg1ODIzZTMxNDUzMjUxNzg2ZmJhZWFlNGE=")
	req.Header.Set("dnt", "1")
	req.Header.Set("ect", "4g")
	req.Header.Set("origin", "https://cn.bing.com")
	req.Header.Set("priority", "u=1, i")
	req.Header.Set("referer", "https://cn.bing.com/search?q=%E7%BF%BB%E8%AF%91&cvid=07c4651890094e5fba232ebb078db1ec&gs_lcrp=EgRlZGdlKgYIABBFGDsyBggAEEUYOzIGCAEQABhAMgYIAhAAGEAyBggDEAAYQDIGCAQQABhAMgYIBRBFGD0yBggGEEUYPTIGCAcQRRg9MgYICBBFGEHSAQgyNzI2ajBqNKgCALACAA&FORM=ANAB01&PC=U531&form=TTSREF&isTTRefreshQuery=1")
	req.Header.Set("sec-ch-ua", `"Chromium";v="130", "Microsoft Edge";v="130", "Not?A_Brand";v="99"`)
	req.Header.Set("sec-ch-ua-arch", `"x86"`)
	req.Header.Set("sec-ch-ua-bitness", `"64"`)
	req.Header.Set("sec-ch-ua-full-version", `"130.0.2849.68"`)
	req.Header.Set("sec-ch-ua-full-version-list", `"Chromium";v="130.0.6723.92", "Microsoft Edge";v="130.0.2849.68", "Not?A_Brand";v="99.0.0.0"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-model", `""`)
	req.Header.Set("sec-ch-ua-platform", `"Windows"`)
	req.Header.Set("sec-ch-ua-platform-version", `"15.0.0"`)
	req.Header.Set("sec-fetch-dest", "empty")
	req.Header.Set("sec-fetch-mode", "cors")
	req.Header.Set("sec-fetch-site", "same-origin")
	req.Header.Set("sec-ms-gec", "2422B64C0FB5B1F6FCE9D34B529E6DCAF0D006DEE052869EB585F2EF78E89026")
	req.Header.Set("sec-ms-gec-version", "1-130.0.2849.68")
	req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0")
	req.Header.Set("x-client-data", "eyIxIjoiNiIsIjEwIjoiXCJKUHdxcFNUbGc2T0dzYjFCT1FOL2dkaUdvKzdoZ1dDcEFGejFNRkFOQXpJPVwiIiwiMiI6IjEiLCIzIjoiMCIsIjQiOiI1NjY4MTAzMTkwODE1NjgzNTM3IiwiNSI6IlwiZlRuTzNIY0VjV3l2bmNNWWhNanFFMEZBUzdDL2dNbm44d0VZQm9rcFFTZz1cIiIsIjYiOiJzdGFibGUiLCI3IjoiMTE1OTY0MTE2OTk0NSIsIjkiOiJkZXNrdG9wIn0=")
	req.Header.Set("x-edge-shopping-flag", "1")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	var ret JSONData

	aerr := json.Unmarshal(bodyText, &ret)

	if aerr != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s : %s\n", word, string(ret[0].Translations[0].Text))
}

数组

数组在 Go 中是固定长度的序列,声明方式类似其他语言。

切片(Slice)

切片是对数组的抽象,不是数组或数组指针,而是通过内部指针和相关属性引用数组的一部分。

package main

import "fmt"

func main() {
	// 通过数组创建切片
	var arr = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	var slice1 []int = arr[2:8] // 部分数组切片
	slice2 := arr[:]             // 全数组切片
	slice3 := arr[5:]            // 从第6个元素起切片

	fmt.Println(slice1)
	fmt.Println(slice2)
	fmt.Println(slice3)

	// 使用 make 创建切片
	var slice0 = make([]int, 100) // 创建长度为100的切片
	fmt.Println(slice0, len(slice0), cap(slice0))
	slice0 = slice3[2:3]
	fmt.Println(slice0, len(slice0), cap(slice0))
}

指针

Go 的指针不能偏移和运算,因此是“安全指针”。Go 中 newmake 都是内存分配的函数:

  • new 只能接收一个参数,返回指向指定类型的指针(类似 C++ 的 new)。

  • make 只能用于创建 slicemapchannel,并返回该类型的本身(非指针)。


map

map 是 Go 中无序的键值对数据结构。map 是引用类型,必须通过 make 初始化后才能使用。

创建 map 示例:

scmap := make(map[int]string)

类似 C++ STL 中的 unordered_map<int, string>

检查键是否存在:

value, ok := map[key]

删除键值对:

delete(map, key)

map 与切片组合使用:

  • map 作为切片元素:

    var mapSlice = make([]map[string]string, 3)
  • 切片作为 map 值:

    var sliceMap = make(map[string][]string, 3)

结构体

定义结构体:

type 类型名 struct {
    字段名 字段类型
    字段名 字段类型
    …
}

实例化结构体:

var 结构体实例 结构体类型

匿名结构体:

用于定义临时数据结构。

var user struct{Name string; Age int}

指针类型结构体:

在 Go 中,结构体指针可以直接使用 . 访问成员。

var p2 = new(person) // 创建指针类型的结构体实例
p3 := &person{}      // 等价于 new(person)

构造函数:

Go 中没有内置构造函数,可以手动实现:

func newPerson(name, city string, age int8) *person {
    return &person{
        name: name,
        city: city,
        age:  age,
    }
}

结构体继承

通过匿名结构体实现类似继承的效果。


方法和接收者

Go 方法是特定类型的函数,其接收者(Receiver)类似于其他语言的 thisself

定义方法:

func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
    // 方法体
}

值类型接收者:

当方法作用于值类型接收者时,Go 会复制一份接收者变量,修改仅作用于副本,无法更改原始变量。

结构体和 JSON 序列化

可以通过 json 包将结构体序列化为 JSON。

// Student 结构体
type Student struct {
    ID     int    `json:"id"` // 使用 tag 指定序列化时的键名
    Gender string // 默认使用字段名作为键名
    name   string // 私有字段无法被 json 包访问
}

使用 json.Marshal 进行序列化:

data, err := json.Marshal(s1)

匿名函数

在Go中,匿名函数是一种没有名称的函数,可以被直接赋值给一个变量。这种函数常用于事件处理、回调函数等场景。

getSqrt := func(a float64) float64 {
    return math.Sqrt(a) // 计算并返回a的平方根
}

在这个例子中,getSqrt 是一个匿名函数,它接受一个 float64 类型的参数 a,并返回 a 的平方根。

闭包

闭包是一个函数和其周围状态(词法环境)的组合。在Go中,闭包可以用来创建具有特定状态的函数。

func a() func() int {
    i := 0
    b := func() int {
        i++
        fmt.Println(i)
        return i
    }
    return b
}

func main() {
    c := a() // c 是一个闭包,它捕获了变量i
    c() // 输出 1
    c() // 输出 2
    c() // 输出 3

    d := a() // d 是一个新的闭包,它有自己的变量i
    d() // 输出 1,不会输出4
}

在这个例子中,函数 a 返回一个闭包 b,这个闭包捕获了变量 i。每次调用 b 时,都会增加 i 的值并打印出来。每次调用 a 都会创建一个新的闭包,它们各自有自己的 i 变量。

延时调用 defer

defer 关键字用于延迟函数的执行,直到包含它的函数即将返回时。defer 可以用于资源清理,如关闭文件、释放锁等。

func main() {
    defer fmt.Println("Deferred function called") // 这将在main函数返回前执行
    fmt.Println("Main function running")
}

在这个例子中,fmt.Println("Deferred function called") 将在 main 函数返回前被调用。如果有多个 defer 语句,它们将按照后进先出(LIFO)的顺序执行。

异常处理

Go语言没有传统的异常处理机制,而是使用 panicrecover 来处理错误。

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in main", r)
        }
    }()
    panic("a problem")
}

在这个例子中,panic("a problem") 抛出了一个错误,defer 语句中的匿名函数捕获了这个错误,并使用 recover 函数恢复了程序的执行。recover 可以捕获 panic 抛出的值,并且只能被 defer 函数调用。

这些代码片段展示了Go语言中匿名函数、闭包、defer 和异常处理的基本用法。希望这些整理和补充对您有所帮助。