您好,登錄后才能下訂單哦!
這篇文章主要介紹“go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)”,在日常操作中,相信很多人在go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
雖然是同一個(gè)世界,但是不同的人站在各自立場(chǎng)看問題,結(jié)果自然會(huì)千人千面,各有不同.
生物學(xué)家會(huì)下意識(shí)對(duì)動(dòng)植物進(jìn)行分類歸納,面向?qū)ο缶幊?/strong>也是如此,用一系列的抽象模型去模擬現(xiàn)實(shí)世界的行為規(guī)律.
數(shù)學(xué)家向來以嚴(yán)謹(jǐn)求學(xué)著稱,作為最重要的基礎(chǔ)科學(xué),數(shù)學(xué)規(guī)律以及歸納演繹方法論對(duì)應(yīng)的就是函數(shù)式編程,不是模擬現(xiàn)實(shí)而是描述規(guī)律更有可能創(chuàng)造規(guī)律.
標(biāo)準(zhǔn)的函數(shù)式編程具有濃厚的數(shù)學(xué)色彩,幸運(yùn)的是,Go
并不是函數(shù)式語言,所以也不必受限于近乎苛責(zé)般的條條框框.
簡(jiǎn)單來說,函數(shù)式編程具有以下特點(diǎn):
不可變性: 不用狀態(tài)變量和可變對(duì)象
函數(shù)只能有一個(gè)參數(shù)
純函數(shù)沒有副作用
摘自維基百科中關(guān)于函數(shù)式編程中有這么一段話:
In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.
上述的英文的大致意思是說:函數(shù)式編程將計(jì)算機(jī)程序看成是數(shù)學(xué)函數(shù)的推演,不用狀態(tài)變量也不用可變對(duì)象來表達(dá)數(shù)與數(shù)之間的關(guān)系.
如需了解詳情,可點(diǎn)擊訪問維基百科關(guān)于函數(shù)式編程 Functional programming 的相關(guān)介紹.
函數(shù)式編程的立足點(diǎn)和出發(fā)點(diǎn)是函數(shù),復(fù)雜函數(shù)是基本函數(shù)經(jīng)過一定組合規(guī)律形成的,所以描述復(fù)雜函數(shù)的過程就是如何拆解重組的過程.
所以接下來我們一邊復(fù)習(xí)一邊學(xué)習(xí)函數(shù)的基本特點(diǎn),為接下來理解函數(shù)式編程打下基礎(chǔ),關(guān)于函數(shù)的基礎(chǔ)語言可參考 go 學(xué)習(xí)筆記之值得特別關(guān)注的基礎(chǔ)語法有哪些
下面以最基本四則運(yùn)算為例,貫穿全文講解函數(shù)的基本語法和高級(jí)特性,力求做到知其然知其所以然.
func
定義普通函數(shù)
eval
函數(shù)定義了加減乘除基本運(yùn)算規(guī)則,若不支持操作類型則拋出異常,終止程序.
func eval(a, b int, op string) int { var result int switch op { case "+": result = a + b case "-": result = a - b case "*": result = a * b case "/": result = a / b default: panic("unsupported operator: " + op) } return result }
測(cè)試未定義操作取余 %
運(yùn)算時(shí),則拋出異常,unsupported operator: %
,說明僅僅支持加減乘除基本運(yùn)算.
func TestEval(t *testing.T) { // 3 -1 2 0 unsupported operator: % t.Log( eval(1, 2, "+"), eval(1, 2, "-"), eval(1, 2, "*"), eval(1, 2, "/"), eval(1, 2, "%"), ) }
多返回值定義標(biāo)準(zhǔn)函數(shù)
Go
語言和其他主流的編程語言明顯不同的是,函數(shù)支持多返回值,通常第一個(gè)返回值表示真正結(jié)果,第二個(gè)返回值表示是否錯(cuò)誤,這也是 Go
關(guān)于異常錯(cuò)誤設(shè)計(jì)的獨(dú)特之處.
如果正常返回,則表示沒有錯(cuò)誤,那么第一個(gè)返回值是正常結(jié)果而第二個(gè)返回值則是空
nil
;如果異常返回,第一個(gè)返回值設(shè)計(jì)無意義的特殊值,第二個(gè)返回值是具體的錯(cuò)誤信息,一般非nil
.
func evalWithStandardStyle(a, b int, op string) (int, error) { switch op { case "+": return a + b, nil case "-": return a - b, nil case "*": return a * b, nil case "/": return a / b, nil default: return 0, fmt.Errorf("unsupported operator: %s", op) } }
改造 eval
函數(shù)以編寫真正 Go
程序,此時(shí)再次測(cè)試,結(jié)果顯示遇到?jīng)]有定義的操作符時(shí)不再拋出異常而是返回默認(rèn)零值以及給出簡(jiǎn)短的錯(cuò)誤描述信息.
func TestEvalWithStandardStyle(t *testing.T) { // Success: 2 if result, err := evalWithStandardStyle(5, 2, "/"); err != nil { t.Log("Error:", err) } else { t.Log("Success:", result) } // Error: unsupported operator: % if result, err := evalWithStandardStyle(5, 2, "%"); err != nil { t.Log("Error:", err) } else { t.Log("Success:", result) } }
其他函數(shù)作為參數(shù)傳入
上例通過多返回值解決了遇到不支持的運(yùn)算符會(huì)報(bào)錯(cuò)終止程序的問題,但是并沒有真正解決問題,假如真的想要進(jìn)行非預(yù)定義的運(yùn)算時(shí),同樣是無能為力!
誰讓你只是使用者而不是設(shè)計(jì)者呢!
那么舞臺(tái)交給你,你就是主角,你想要怎么處理輸入怎么輸出就怎么處理,全部邏輯轉(zhuǎn)移給使用者,這樣就不存在無法滿足需求的情況了.
func evalWithApplyStyle(a, b int, op func(int, int) (int, error)) (int, error) { return op(a, b) }
操作符由原來的字符串
string
更改成函數(shù)func(int, int) (int, error)
,舞臺(tái)交給你,全靠自由發(fā)揮!
evalWithApplyStyle
函數(shù)內(nèi)部直接調(diào)用函數(shù)參數(shù) op
并返回該函數(shù)的處理結(jié)果,當(dāng)前演示示例中函數(shù)的控制權(quán)完全轉(zhuǎn)移給函數(shù)入?yún)?op
函數(shù),實(shí)際情況可按照實(shí)際需求決定如何處理 evalWithApplyStyle
邏輯.
func divide(a, b int) (int, error) { return a / b, nil } func mod(a, b int) (int, error) { return a % b, nil }
自己動(dòng)手,豐衣足食,順手定義除法 divide
和取余 mod
運(yùn)算,接下來測(cè)試下實(shí)現(xiàn)效果.
func TestEvalWithApplyStyle(t *testing.T) { // Success: 2 if result, err := evalWithApplyStyle(5, 2, divide); err != nil { t.Log("Error:", err) } else { t.Log("Success:", result) } // Success: 1 if result, err := evalWithApplyStyle(5, 2, mod); err != nil { t.Log("Error:", err) } else { t.Log("Success:", result) } }
測(cè)試結(jié)果很理想,不僅實(shí)現(xiàn)了減加乘除等基本運(yùn)算,還可以實(shí)現(xiàn)之前一直沒法實(shí)現(xiàn)的取余運(yùn)算!
這說明了這種函數(shù)作為參數(shù)的做法充分調(diào)動(dòng)勞動(dòng)人民積極性,媽媽再也不用擔(dān)心我無法實(shí)現(xiàn)復(fù)雜功能了呢!
匿名函數(shù)也可以作為參數(shù)
一般而言,調(diào)用函數(shù)時(shí)都是直接用函數(shù)名進(jìn)行調(diào)用,單獨(dú)的函數(shù)具有可復(fù)用性,但如果本就是一次性函數(shù)的話,其實(shí)是沒必要定義帶函數(shù)名形式的函數(shù).
依然是上述例子,這一次對(duì)兩個(gè)數(shù)的運(yùn)算規(guī)則不再是數(shù)學(xué)運(yùn)算了,這一次我們來比較兩個(gè)數(shù)的最大值,使用匿名函數(shù)的形式進(jìn)行實(shí)現(xiàn).
func TestEvalWithApplyStyle(t *testing.T) { // Success: 5 if result, err := evalWithApplyStyle(5, 2, func(a int, b int) (result int, e error) { if a > b { return a, nil } return b, nil }); err != nil { t.Log("Error:", err) } else { t.Log("Success:", result) } }
函數(shù)的返回值可以是函數(shù)
依然是上述示例,如果由于原因不需要立即返回函數(shù)的計(jì)算結(jié)果而是等待使用者自己覺得時(shí)機(jī)合適的時(shí)候再計(jì)算返回值,這時(shí)候函數(shù)返回值依然是函數(shù)就很有作用了,也就是所謂的惰性求值.
func evalWithFunctionalStyle(a, b int, op func(int, int) (int, error)) func() (int, error) { return func() (int, error) { return op(a, b) } }
上述函數(shù)看起來可能有點(diǎn)難以理解,實(shí)際上相對(duì)于上例僅僅更改了返回值,由原來的 (int, error)
更改成 func() (int, error)
,其余均保持不變喲!
evalWithFunctionalStyle
函數(shù)依然是使用者的主場(chǎng),和上例相比的唯一不同之處在于,你的主場(chǎng)你做主,什么時(shí)候裁判完全自己說了算,并不是運(yùn)行后就立馬宣布結(jié)果.
func pow(a, b int) (int, error) { return int(math.Pow(float64(a), float64(b))),nil } func TestEvalWithFunctionalStyle(t *testing.T) { ef := evalWithFunctionalStyle(5, 2, pow) time.Sleep(time.Second * 1) // Success: 25 if result, err := ef(); err != nil { t.Log("Error:", err) } else { t.Log("Success:", result) } }
time.Sleep(time.Second * 1)
演示代碼代表執(zhí)行 evalWithFunctionalStyle
函數(shù)后可以不立即計(jì)算最終結(jié)果,等待時(shí)機(jī)合適后由使用者再次調(diào)用 ef()
函數(shù)進(jìn)行惰性求值.
// 1 1 2 3 5 8 13 21 34 55 // a b // a b func fibonacci() func() int { a, b := 0, 1 return func() int { a, b = b, a+b return a } }
函數(shù)可以充當(dāng)類型
上述示例中講解了函數(shù)可以作為返回值,參數(shù)有函數(shù),返回值也有參數(shù),所以 evalWithFunctionalStyle
函數(shù)看起來比較費(fèi)勁,而 Go
語言的類型別名就是為了簡(jiǎn)化而生的,更何況函數(shù)是 Go
中的一等公民,當(dāng)然也適合了.
func evalWithFunctionalStyle(a, b int, op func(int, int) (int, error)) func() (int, error) { return func() (int, error) { return op(a, b) } }
于是打算把入?yún)⒑瘮?shù) func(int, int) (int, error)
和返回值函數(shù) func() (int, error)
進(jìn)行統(tǒng)一,而入?yún)⒑瘮?shù)和返回值函數(shù)唯一不同之處就是入?yún)€(gè)數(shù)不同,所以順理成章想到了 Go
函數(shù)中的不定長(zhǎng)參數(shù)相關(guān)語法.
type generateIntFunc func(base ...int) (int, error)
這樣入?yún)⒑瘮?shù)和出參函數(shù)都可以用 generateIntFunc
類型函數(shù)進(jìn)行替代,接著改造 evalWithFunctionalStyle
函數(shù).
func evalWithObjectiveStyle(a, b int, op generateIntFunc) generateIntFunc { return func(base ...int) (i int, e error) { return op(a, b) } }
改造后的 evalWithObjectiveStyle
函數(shù)看起來比較簡(jiǎn)潔,花花架子中看是否中用還不好說,還是用測(cè)試用例說話吧!
func TestEvalWithObjectiveStyle(t *testing.T) { ef := evalWithObjectiveStyle(5, 2, func(base ...int) (int,error) { result := 0 for i := range base { result += base[i] } return result,nil }) time.Sleep(time.Second * 1) // Success: 7 if result, err := ef(); err != nil { t.Log("Error:", err) } else { t.Log("Success:", result) } }
函數(shù)別名進(jìn)行類型化后并不影響功能,依然是函數(shù)式編程,不過夾雜了些面向?qū)ο蟮奈兜?
類型化函數(shù)可以實(shí)現(xiàn)接口
函數(shù)通過別名形式進(jìn)行類型化后可以實(shí)現(xiàn)接口,某些程度上可以視為一種類型,因此實(shí)現(xiàn)接口也是順理成章的事情.
func (g generateIntFunc) String() string { r,_ := g() return fmt.Sprint(r) }
此處示例代碼中為類型化函數(shù)
generateIntFunc
實(shí)現(xiàn)String
接口方法,可能并沒有太大實(shí)際意義,僅僅是為了講解這個(gè)知識(shí)點(diǎn)而硬湊上去的,實(shí)際情況肯定會(huì)有所不同.
func TestEvalWithInterfaceStyle(t *testing.T) { ef := evalWithObjectiveStyle(5, 2, func(base ...int) (int,error) { result := 0 for i := range base { result += base[i] } return result,nil }) time.Sleep(time.Second * 1) // String: 7 t.Log("String:", ef.String()) // Success: 7 if result, err := ef(); err != nil { t.Log("Error:", err) } else { t.Log("Success:", result) } }
惰性求值獲取的函數(shù)變量 ef
此時(shí)可以調(diào)用 String
方法,也就是具備對(duì)象化能力,得到的最終結(jié)果竟然和直接運(yùn)行該函數(shù)的值一樣?
有點(diǎn)神奇,目前還不理解這是什么操作,如果有 Go
語言的大佬們不吝賜教的話,小弟感激不盡!
水到渠成的閉包
函數(shù)的參數(shù),返回值都可以是另外的函數(shù),函數(shù)也可以作為引用那樣傳遞給變量,也存在匿名函數(shù)等簡(jiǎn)化形式,除此之外,類型化后的函數(shù)還可以用來實(shí)現(xiàn)接口等等特性應(yīng)該足以闡釋一等公民的高貴身份地位了吧?
如此強(qiáng)大的函數(shù)特性,只要稍加組合使用就會(huì)擁有強(qiáng)大的能力,并且 Go
語言并不是嚴(yán)格的函數(shù)式語言,沒有太多語法層面的限制.
// 1 1 2 3 5 8 13 21 34 55 // a b // a b func fibonacci() func() int { a, b := 0, 1 return func() int { a, b = b, a+b return a } }
斐波那契數(shù)列函數(shù) fibonacci
的返回值是真正的生成器函數(shù),每次調(diào)用都會(huì)生成新的斐波那契數(shù)字.
這就是 Go
語言實(shí)現(xiàn)閉包的一種簡(jiǎn)單示例,fibonacci
函數(shù)本身的變量 a,b
被內(nèi)部匿名函數(shù) func() int
所引用,而這種引用最終被使用者不斷調(diào)用就會(huì)導(dǎo)致最初的 a,b
變量一直被占用著,只要繼續(xù)調(diào)用這種生成器,裴波那契數(shù)列的數(shù)字就會(huì)一直遞增.
// 1 1 2 3 5 8 13 21 34 55 func TestFibonacci(t *testing.T) { f := fibonacci() for i := 0; i < 10; i++ { fmt.Print(f(), " ") } fmt.Println() }
func TestFibonacci(t *testing.T) { f := fibonacci() for i := 0; i < 10; i++ { fmt.Print(f(), " ") } fmt.Println() }
函數(shù)是一等公民,其中函數(shù)參數(shù),變量,函數(shù)返回值都可以是函數(shù).
高階函數(shù)是普通函數(shù)組合而成,參數(shù)和返回值可以是另外的函數(shù).
函數(shù)是函數(shù)式編程的基礎(chǔ),支持函數(shù)式編程但并不是函數(shù)式語言.
沒有純粹函數(shù)式編程的條條框框,更加靈活自由,良好的可讀性.
到此,關(guān)于“go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。