GO—函數

Go 語言支持普通函數、匿名函數和閉包,從設計上對函數進行了優化和改進,讓函數使用起來更加方便。

Go 語言的函數屬于“一等公民”(first-class),也就是說:

  • 函數本身可以作為值進行傳遞。
  • 支持匿名函數和閉包(closure)。
  • 函數可以滿足接口。

函數定義:

func function_name( [parameter list] ) [return_types] {函數體
}
  • func:函數由 func 開始聲明
  • function_name:函數名稱,函數名和參數列表一起構成了函數簽名。
  • parameter list:參數列表,參數就像一個占位符,當函數被調用時,你可以將值傳遞給參數,這個值被稱為實際參數。參數列表指定的是參數類型、順序、及參數個數。參數是可選的,也就是說函數也可以不包含參數。
  • return_types:返回類型,函數返回一列值。return_types 是該列值的數據類型。有些功能不需要返回值,這種情況下 return_types 不是必須的。
  • 函數體:函數定義的代碼集合。

返回值可以為多個:

func test(x, y int, s string) (int, string) {// 類型相同的相鄰參數,參數類型可合并。 多返回值必須用括號。n := x + y          return n, fmt.Sprintf(s, n)
}

1.1 函數做為參數

函數做為一等公民,可以做為參數傳遞。

func fn() int {return 300
}
func test(fn func() int) int {return fn()
}
func main() {//這是直接使用匿名函數s := test(func() int { return 200 })fmt.Println(s) //200//傳入一個函數s2 := test(fn)fmt.Println(s2) //300
}

在將函數做為參數的時候,我們可以使用類型定義,將函數定義為類型,這樣便于閱讀

type myFunc func(s string, x, y int) stringfunc format(fn myFunc, s string, x, y int) string {return fn(s, x, y)
}func formatFunc(s string, x, y int) string {return fmt.Sprintf(s, x, y)
}
func main() {s2 := format(formatFunc, "%d %d", 10, 20)fmt.Println(s2)
}

1.2 函數返回值

函數返回值可以有多個,同時Go支持對返回值命名

func main() {var a, b = 10, 20fmt.Println(sum(a, b))
}
//多個返回值 用括號擴起來
func sum(a, b int) (int, int) {return a, b
}

.3 參數

函數定義時指出,函數定義時有參數,該變量可稱為函數的形參。

形參就像定義在函數體內的局部變量。

但當調用函數,傳遞過來的變量就是函數的實參,函數可以通過兩種方式來傳遞參數:

  1. 值傳遞:指在調用函數時將實際參數復制一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數。
func swap(x, y int) int {... ...}

  1. 引用傳遞:是指在調用函數時將實際參數的地址傳遞到函數中,那么在函數中對參數所進行的修改,將影響到實際參數。
package mainimport ("fmt"
)/* 定義相互交換值的函數 */
func swap(x, y *int) {*x,*y = *y,*x
}func main() {var a, b int = 1, 2/*調用 swap() 函數&a 指向 a 指針,a 變量的地址&b 指向 b 指針,b 變量的地址*/swap(&a, &b)fmt.Println(a, b)
}

map、slice、chan、指針、interface默認以引用的方式傳遞。

不定參數傳值

不定參數傳值 就是函數的參數不是固定的,后面的類型是固定的。(可變參數)

Golang 可變參數本質上就是 slice。只能有一個,且必須是最后一個。

在參數賦值時可以不用用一個一個的賦值,可以直接傳遞一個數組或者切片。

格式:

func test1(args ...int) { //0個或多個參數}func test2(a int, args ...int) int { //1個或多個參數}func test3(a int, b int, args ...int) int {   //2個或多個參數}

注意:其中args是一個slice,我們可以通過arg[index]依次訪問所有參數,通過len(arg)來判斷傳遞參數的個數.

func main() {a := []int{10, 20, 10}fmt.Println(test("sum: %d", a...)) // slice... 展開slice
}func test(s string, n ...int) string {var x = 0for _, v := range n {x += v}return fmt.Sprintf(s, x)
}

2. 匿名函數

匿名函數是指不需要定義函數名的一種函數實現方式。

在Go里面,函數可以像普通變量一樣被傳遞或使用,Go語言支持隨時在代碼里定義匿名函數。

匿名函數由一個不帶函數名的函數聲明和函數體組成。匿名函數的優越性在于可以直接使用函數內的變量,不必聲明。

匿名函數的定義格式如下:

func(參數列表)(返回參數列表){函數體
}

func main() {//這里將一個函數當做一個變量一樣的操作。getSqrt := func(a float64) float64 { return math.Sqrt(a)}fmt.Println(getSqrt(4.123))
}

在定義時調用匿名函數

匿名函數可以在聲明后調用,例如:

func main() {func(a int) {fmt.Println(a)}(100) //(100),表示對匿名函數進行調用,傳遞參數為 100。
}

返回多個匿名函數

package mainimport "fmt"func FGen(x, y int) (func() int, func(int) int) {//求和的匿名函sum := func() int {return x + y}// (x+y) *z 的匿名函數avg := func(z int) int {return (x + y) * z}return sum, avg
}func main() {f1, f2 := FGen(1, 2)fmt.Println(f1())fmt.Println(f2(3))
}

3. 閉包

所謂“閉包”,指的是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。

閉包=函數+引用環境

package mainimport "fmt"func main() {// 創建一個玩家生成器generator := playGen("碼神")// 返回玩家的名字和血量name, hp := generator()// 打印值fmt.Println(name, hp)generator1 := playGen2()name1, hp1 := generator1("碼神")// 打印值fmt.Println(name1, hp1)
}// 創建一個玩家生成器, 輸入名稱, 輸出生成器
func playGen(s string) func() (string, int) {// 血量一直為150hp := 150// 返回創建的閉包return func() (string, int) {// 將變量引用到閉包中return s, hp}
}// 創建一個玩家生成器, 輸入名稱, 輸出生成器
func playGen2() func(name string) (string, int) {hp := 150return func(name string) (string, int) {return name, hp}
}

. 延遲調用

Go語言的 defer 語句會將其后面跟隨的語句進行延遲處理

defer特性:

  1. 關鍵字 defer 用于注冊延遲調用。
  2. 這些調用直到 return 前才被執。因此,可以用來做資源清理。
  3. 多個defer語句,按先進后出的方式執行。
  4. defer語句中的變量,在defer聲明時就決定了。

defer的用途:

  1. 關閉文件句柄
  2. 鎖資源釋放
  3. 數據庫連接釋放

go 語言的defer功能強大,對于資源管理非常方便,但是如果沒用好,也會有陷阱。

package mainimport ("log""time"
)func main() {start := time.Now()log.Printf("開始時間為:%v", start)defer log.Printf("時間差:%v", time.Since(start))  // Now()此時已經copy進去了//不受這3秒睡眠的影響time.Sleep(3 * time.Second)log.Printf("函數結束")
}
  • Go 語言中所有的函數調用都是傳值的
  • 調用 defer 關鍵字會立刻拷貝函數中引用的外部參數 ,包括start 和time.Since中的Now
  • defer的函數在壓棧的時候也會保存參數的值,并非在執行時取值。

如何解決上述問題:使用defer func()

func main() {start := time.Now()defer func() {log.Printf("開始調用defer")log.Printf("時間差:%v", time.Since(start))log.Printf("結束調用defer")}()time.Sleep(3 * time.Second)log.Printf("函數結束")
}

因為拷貝的是函數指針,函數屬于引用傳遞

另一個問題:

func main() {var whatever = [5]int{1, 2, 3, 4, 5}for i, _ := range whatever {//函數正常執行,由于閉包用到的變量 i 在執行的時候已經變成4,所以輸出全都是4.defer func() { fmt.Println(i) }()}
}

解決:

func main() {var whatever = [5]int{1, 2, 3, 4, 5}for i, _ := range whatever {i := idefer func() { fmt.Println(i) }()}
}

5. 異常處理

Go語言中使用 panic 拋出錯誤,recover 捕獲錯誤。

異常的使用場景簡單描述:Go中可以拋出一個panic的異常,然后在defer中通過recover捕獲這個異常,然后正常處理。

panic:

  1. 內置函數
  2. 假如函數F中書寫了panic語句,會終止其后要執行的代碼,在panic所在函數F內如果存在要執行的defer函數列表,按照defer的逆序執行
  3. 返回函數F的調用者G,在G中,調用函數F語句之后的代碼不會執行,假如函數G中存在要執行的defer函數列表,按照defer的逆序執行
  4. 直到goroutine整個退出,并報告錯誤

recover:

  1. 內置函數
  2. 用來捕獲panic,從而影響應用的行為

golang 的錯誤處理流程:當一個函數在執行過程中出現了異常或遇到 panic(),正常語句就會立即終止,然后執行 defer 語句,再報告異常信息,最后退出 goroutine。如果在 defer 中使用了 recover() 函數,則會捕獲錯誤信息,使該錯誤信息終止報告。

注意:

  1. 利用recover處理panic指令,defer 必須放在 panic 之前定義,另外 recover 只有在 defer 調用的函數中才有效。否則當panic時,recover無法捕獲到panic,無法防止panic擴散。
  2. recover 處理異常后,邏輯并不會恢復到 panic 那個點去,函數跑到 defer 之后的那個點。
  3. 多個 defer 會形成 defer 棧,后定義的 defer 語句會被最先調用。

func main() {test()
}func test() {defer func() {if err := recover(); err != nil {log.Println(err) //2024/02/28 23:03:38 print out panic}}()panic("print out panic")}

由于 panic、recover 參數類型為 interface{},因此可拋出任何類型對象。

func panic(v interface{})func recover() interface{}

延遲調用中引發的錯誤,可被后續延遲調用捕獲,但僅最后一個錯誤可被捕獲:

func test() {defer func() {// defer panic 會打印fmt.Println(recover())  //defer panic}()defer func() {panic("defer panic")}()panic("test panic")
}func main() {test()
}

除用 panic 引發中斷性錯誤外,還可返回 error 類型錯誤對象來表示函數調用狀態:

type error interface {Error() string
}

標準庫 errors.New 和 fmt.Errorf 函數用于創建實現 error 接口的錯誤對象。通過判斷錯誤對象實例來確定具體錯誤類型。

package mainimport ("errors""fmt""log"
)var divError = errors.New("div error:分母不能為0")func div(x, y int) (int, error) {if y == 0 {return 0, divError  }return x / y, nil
}func main() {defer func() {if err := recover(); err != nil {log.Fatalln(err)  //2024/02/28 23:17:14 div error:分母不能為0}}()res, err := div(8, 0)switch err {case nil:fmt.Println(res)case divError:panic(err)}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/715181.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/715181.shtml
英文地址,請注明出處:http://en.pswp.cn/news/715181.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Leetcode.2369 檢查數組是否存在有效劃分

題目鏈接 Leetcode.2369 檢查數組是否存在有效劃分 rating : 1780 題目描述 給你一個下標從 0 0 0 開始的整數數組 n u m s nums nums ,你必須將數組劃分為一個或多個 連續 子數組。 如果獲得的這些子數組中每個都能滿足下述條件 之一 ,則可以稱其為…

推薦6款SSH遠程連接工具

1、Xshell 介紹: xshell是一個非常強大的安全終端模擬軟件,它支持SSH1, SSH2, 以及Windows平臺的TELNET 協議。Xshell可以在Windows界面下用來訪問遠端不同系統下的服務器,從而比較好的達到遠程控制終端的目的。 業界最強大的SSH客戶機 官…

數據分析-Pandas數據的直方圖探查

數據分析-Pandas數據的直方圖探查 數據分析和處理中,難免會遇到各種數據,那么數據呈現怎樣的規律呢?不管金融數據,風控數據,營銷數據等等,莫不如此。如何通過圖示展示數據的規律? 數據表&…

農產品質量追溯系統—功能介紹(2)

儲藏管理 儲藏信息管理對需要儲藏的農產品,記錄儲藏的相關信息,如儲藏開始時間、存放倉庫、操作人員、儲藏原因等; 倉庫信息管理物流管理 物流公司管理對相關的物流公司信息進行登記,以便于管理和追溯; 車輛管理

我的秋招數據分析崗面經分享(京東,美團,阿里,拼多多,vivo,滴滴)

節前,我們社群組織了一場技術&面試討論會,邀請了一些互聯網大廠同學、參加社招和校招面試的同學,針對新手如何入門數據分析、機器學習算法、該如何備戰面試、面試常考點分享等熱門話題進行了深入的討論。 基于社群的討論,今天…

力扣爆刷第84天之hot100五連刷6-10

力扣爆刷第84天之hot100五連刷6-10 文章目錄 力扣爆刷第84天之hot100五連刷6-10一、15. 三數之和二、42. 接雨水三、3. 無重復字符的最長子串四、438. 找到字符串中所有字母異位詞五、560. 和為 K 的子數組 一、15. 三數之和 題目鏈接:https://leetcode.cn/problem…

JAVA學習筆記13(位運算)

1.位運算 1.1 原碼、反碼、補碼 ? *規則: ? 1.二進制的最高位是符號位:0表示正數,1表示負數 ? 2.正數的原碼,反碼,補碼都一樣(三碼合一) ? 3.負數的反碼 他的原碼符號位不變&#xff…

從metashape導出深度圖,從深度圖恢復密集點云

從metashape導出深度圖,從深度圖恢復密集點云 1.從metashape導出深度圖 參考:https://blog.csdn.net/WHU_StudentZhong/article/details/123107072?spm1001.2014.3001.5502 2.從深度圖建立密集點云 首先從metashape導出blockExchange格式的xml文件&…

OpenHarmony、HarmonyOS打開編輯 PDF 等操作的三方組件使用教程

項目場景: 隨著數字化時代的發展,PDF 文檔成為廣泛應用于各行業的重要文件格式。為了提高OpenHarmony/HarmonyOS生態系統的功能性和用戶體驗,我們需要一款支持打開、編輯PDF文件的應用程序。 使用戶能夠輕松打開、瀏覽和編輯PDF文件。該應用將充分利用OpenHarmony/HarmonyO…

【NTN 衛星通信】衛星和無人機配合的應用場景

1 場景概述 衛星接入網是一種有潛力的技術,可以為地面覆蓋差地區的用戶提供無處不在的網絡服務。然而,衛星覆蓋范圍對于位于考古或采礦地點內部/被茂密森林覆蓋的村莊/山谷/靠近山丘或大型建筑物的用戶可能很稀疏。因此,涉及衛星接入和無人駕…

HarmonyOS Full SDK的安裝

OpenHarmony的應用開發工具HUAWEI DevEco Studio現在隨著OpenHarmony版本發布而發布,只能在版本發布說明中下載,例如最新版本的OpenHarmony 4.0 Release。對應的需要下載DevEco Studio 4.0 Release,如下圖。 圖片 下載Full SDK主要有兩種方式,一種是通過DevEco Studio下載…

教你用Fiddler捕獲HTTPS請求

安裝Fiddler 這里不特別說明了,網上搜索一大把,根據安裝引導一步步安裝即可。(這里采用的是fiddler v4.6) 配置Fiddler 1、打開fiddler配置Tools –>Telerik Fiddler Options。 2、打開HTTPS配置項,勾選“Captur…

【程序員養生延壽系列-萬人關注的養生指南 4 】

1.早起一杯溫水,疏通腸胃,補充水分。 2.早十點和下午三點左右活動活動身體(運動or健身),放松緊張疲憊的身體,幫助消化,給身體透個氣。 3.每天散步,好處多多(減肥健身&a…

ctf_show筆記篇(web入門---爆破)

爆破 21:直接bp抓包跑字典,需base64加密 22:可用工具跑也可用瀏覽器找還可以用網上做好的域名查找去找 23:此題需跑腳本已經附上自寫腳本 最后跑出來六個答案一個一個嘗試得到答案為3j import hashlibm "0123456789qwert…

C++_AVL樹

目錄 1、AVL的概念 2、平衡因子的調整概念 3、AVL樹的插入 3.1 調整平衡因子代碼實現 3.2 右旋操作 3.2 左旋操作 3.3 雙旋-先右旋再左旋 3.4 雙旋-先左旋再右旋 3.5 旋轉操作的小結 4、AVL的驗證與實現 結語 前言: 在C中,AVL樹是在二叉搜索…

2024中國眼博會,山東省眼科醫學技術交流大會

以展帶會,以會促展,展與會有機結合,立足山東打造具全國影響力的眼康產業發展盛會; ——隨著時代的高速發展,科技的進步,現代生活節奏的加快,青少年近視問題日益嚴重,對兒童青少年的…

舊的Spring Security OAuth已停止維護,全面擁抱新解決方案Spring SAS

Spring Authorization Server 替換 Shiro 指引 背景 Spring 團隊正式宣布 Spring Security OAuth 停止維護,該項目將不會再進行任何的迭代 目前 Spring 生態中的 OAuth2 授權服務器是 Spring Authorization Server 已經可以正式生產使用作為 SpringBoot 3.0 的最新…

如何使用naive 做一個模態框的方式

1.我的問題使用了一個table 表格&#xff0c;在表格中設置倆個按鈕 最后做出來的效果 <template><div><h1>測試文件</h1><!-- 表格 --><n-data-table :columns"columns" :data"data" :pagination"pagination" …

Linux內核隊列queue.h

文章目錄 一、簡介二、SLIST單向無尾鏈表2.1 介紹2.2 操作2.3 例子 三、STAILQ單向有尾鏈表四、LIST雙向無尾鏈表五、TAILQ雙向有尾鏈表六、CIRCLEQ循環鏈表七、queue源碼參考 一、簡介 queue.h是一個非常經典的文件&#xff0c;定義了一系列宏的操作&#xff0c;它定義了一系…

筆記72:關于IMU(慣性測量單元)傳感器的作用【不涉及公式推導】

一、IMU傳感器是什么&#xff1a; 慣性測量單元IMU&#xff08;Inertial Measurement Unit&#xff09;是一種使用【加速度計】和【陀螺儀】來測量【物體三軸姿態角&#xff08;空間姿態&#xff09;】的裝置&#xff1b;IMU在坐標系的每個坐標軸上&#xff0c;均安裝有1個陀螺…