溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)

發(fā)布時(shí)間:2021-06-23 13:40:40 來源:億速云 閱讀:488 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要介紹“go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)”,在日常操作中,相信很多人在go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

生物學(xué)家和數(shù)學(xué)家的立場(chǎng)不同

雖然是同一個(gè)世界,但是不同的人站在各自立場(chǎng)看問題,結(jié)果自然會(huì)千人千面,各有不同.

生物學(xué)家會(huì)下意識(shí)對(duì)動(dòng)植物進(jìn)行分類歸納,面向?qū)ο缶幊?/strong>也是如此,用一系列的抽象模型去模擬現(xiàn)實(shí)世界的行為規(guī)律.

go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)

數(shù)學(xué)家向來以嚴(yán)謹(jǐn)求學(xué)著稱,作為最重要的基礎(chǔ)科學(xué),數(shù)學(xué)規(guī)律以及歸納演繹方法論對(duì)應(yīng)的就是函數(shù)式編程,不是模擬現(xiàn)實(shí)而是描述規(guī)律更有可能創(chuàng)造規(guī)律.

go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)

標(biāo)準(zhǔn)的函數(shù)式編程具有濃厚的數(shù)學(xué)色彩,幸運(yùn)的是,Go 并不是函數(shù)式語言,所以也不必受限于近乎苛責(zé)般的條條框框.

簡(jiǎn)單來說,函數(shù)式編程具有以下特點(diǎn):

  • 不可變性: 不用狀態(tài)變量和可變對(duì)象

  • 函數(shù)只能有一個(gè)參數(shù)

  • 純函數(shù)沒有副作用

go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)

摘自維基百科中關(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ǔ)語法有哪些

函數(shù)的基礎(chǔ)語法和高級(jí)特性

下面以最基本四則運(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()
}

go語言的函數(shù)基礎(chǔ)語法和高級(jí)特性總結(jié)

函數(shù)式編程入門函數(shù)總結(jié)

  • 函數(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í)用的文章!

向AI問一下細(xì)節(jié)

免責(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)容。

AI