<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ?Go 經(jīng)典入門系列 33:函數(shù)是一等公民(頭等函數(shù))

          共 7436字,需瀏覽 15分鐘

           ·

          2021-01-05 01:09

          點擊上方藍(lán)色“Go語言中文網(wǎng)”關(guān)注,每天一起學(xué) Go

          first class functions

          歡迎來到 Golang 系列教程[1]的第 33 篇。

          什么是頭等函數(shù)?

          支持頭等函數(shù)(First Class Function)的編程語言,可以把函數(shù)賦值給變量,也可以把函數(shù)作為其它函數(shù)的參數(shù)或者返回值。Go 語言支持頭等函數(shù)的機(jī)制

          本教程我們會討論頭等函數(shù)的語法和用例。

          匿名函數(shù)

          我們來編寫一個簡單的示例,把函數(shù)[2]賦值給一個變量[3]

          package?main

          import?(
          ????"fmt"
          )

          func?main()?{
          ????a?:=?func()?{
          ????????fmt.Println("hello?world?first?class?function")
          ????}
          ????a()
          ????fmt.Printf("%T",?a)
          }

          在 playground 上運行[4]

          在上面的程序中,我們將一個函數(shù)賦值給了變量 a(第 8 行)。這是把函數(shù)賦值給變量的語法。你如果觀察得仔細(xì)的話,會發(fā)現(xiàn)賦值給 a 的函數(shù)沒有名稱。由于沒有名稱,這類函數(shù)稱為匿名函數(shù)(Anonymous Function)

          調(diào)用該函數(shù)的唯一方法就是使用變量 a。我們在下一行調(diào)用了它。a() 調(diào)用了這個函數(shù),打印出 hello world first class function。在第 12 行,我們打印出 a 的類型。這會輸出 func()

          運行該程序,會輸出:

          hello?world?first?class?function
          func()

          要調(diào)用一個匿名函數(shù),可以不用賦值給變量。通過下面的例子,我們看看這是怎么做到的。

          package?main

          import?(
          ????"fmt"
          )

          func?main()?{
          ????func()?{
          ????????fmt.Println("hello?world?first?class?function")
          ????}()
          }

          在 playground 上運行[5]

          在上面的程序中,第 8 行定義了一個匿名函數(shù),并在定義之后,我們使用 () 立即調(diào)用了該函數(shù)(第 10 行)。該程序會輸出:

          hello?world?first?class?function

          就像其它函數(shù)一樣,還可以向匿名函數(shù)傳遞參數(shù)。

          package?main

          import?(
          ????"fmt"
          )

          func?main()?{
          ????func(n?string)?{
          ????????fmt.Println("Welcome",?n)
          ????}("Gophers")
          }

          在 playground 上運行[6]

          在上面的程序中,我們向匿名函數(shù)傳遞了一個字符串參數(shù)(第 10 行)。運行該程序后會輸出:

          Welcome?Gophers

          用戶自定義的函數(shù)類型

          正如我們定義自己的結(jié)構(gòu)體[7]類型一樣,我們可以定義自己的函數(shù)類型。

          type?add?func(a?int,?b?int)?int

          以上代碼片段創(chuàng)建了一個新的函數(shù)類型 add,它接收兩個整型參數(shù),并返回一個整型。現(xiàn)在我們來定義 add 類型的變量。

          我們來編寫一個程序,定義一個 add 類型的變量。

          package?main

          import?(
          ????"fmt"
          )

          type?add?func(a?int,?b?int)?int

          func?main()?{
          ????var?a?add?=?func(a?int,?b?int)?int?{
          ????????return?a?+?b
          ????}
          ????s?:=?a(5,?6)
          ????fmt.Println("Sum",?s)
          }

          在 playground 上運行[8]

          在上面程序的第 10 行,我們定義了一個 add 類型的變量 a,并向它賦值了一個符合 add 類型簽名的函數(shù)。我們在第 13 行調(diào)用了該函數(shù),并將結(jié)果賦值給 s。該程序會輸出:

          Sum?11

          高階函數(shù)

          wiki[9] 把高階函數(shù)(Hiher-order Function)定義為:滿足下列條件之一的函數(shù)

          • 接收一個或多個函數(shù)作為參數(shù)
          • 返回值是一個函數(shù)

          針對上述兩種情況,我們看看一些簡單實例。

          把函數(shù)作為參數(shù),傳遞給其它函數(shù)

          package?main

          import?(
          ????"fmt"
          )

          func?simple(a?func(a,?b?int)?int)?{
          ????fmt.Println(a(60,?7))
          }

          func?main()?{
          ????f?:=?func(a,?b?int)?int?{
          ????????return?a?+?b
          ????}
          ????simple(f)
          }

          在 playground 上運行[10]

          在上面的實例中,第 7 行我們定義了一個函數(shù) simplesimple 接收一個函數(shù)參數(shù)(該函數(shù)接收兩個 int 參數(shù),返回一個 a 整型)。在 main 函數(shù)的第 12 行,我們創(chuàng)建了一個匿名函數(shù) f,其簽名符合 simple 函數(shù)的參數(shù)。我們在下一行調(diào)用了 simple,并傳遞了參數(shù) f。該程序打印輸出 67。

          在其它函數(shù)中返回函數(shù)

          現(xiàn)在我們重寫上面的代碼,在 simple 函數(shù)中返回一個函數(shù)。

          package?main

          import?(
          ????"fmt"
          )

          func?simple()?func(a,?b?int)?int?{
          ????f?:=?func(a,?b?int)?int?{
          ????????return?a?+?b
          ????}
          ????return?f
          }

          func?main()?{
          ????s?:=?simple()
          ????fmt.Println(s(60,?7))
          }

          在 playground 上運行[11]

          在上面程序中,第 7 行的 simple 函數(shù)返回了一個函數(shù),并接受兩個 int 參數(shù),返回一個 int

          在第 15 行,我們調(diào)用了 simple 函數(shù)。我們把 simple 的返回值賦值給了 s。現(xiàn)在 s 包含了 simple 函數(shù)返回的函數(shù)。我們調(diào)用了 s,并向它傳遞了兩個 int 參數(shù)(第 16 行)。該程序輸出 67。

          閉包

          閉包(Closure)是匿名函數(shù)的一個特例。當(dāng)一個匿名函數(shù)所訪問的變量定義在函數(shù)體的外部時,就稱這樣的匿名函數(shù)為閉包。

          看看一個示例就明白了。

          package?main

          import?(
          ????"fmt"
          )

          func?main()?{
          ????a?:=?5
          ????func()?{
          ????????fmt.Println("a?=",?a)
          ????}()
          }

          在 playground 上運行[12]

          在上面的程序中,匿名函數(shù)在第 10 行訪問了變量 a,而 a 存在于函數(shù)體的外部。因此這個匿名函數(shù)就是閉包。

          每一個閉包都會綁定一個它自己的外圍變量(Surrounding Variable)。我們通過一個簡單示例來體會這句話的含義。

          package?main

          import?(
          ????"fmt"
          )

          func?appendStr()?func(string)?string?{
          ????t?:=?"Hello"
          ????c?:=?func(b?string)?string?{
          ????????t?=?t?+?"?"?+?b
          ????????return?t
          ????}
          ????return?c
          }

          func?main()?{
          ????a?:=?appendStr()
          ????b?:=?appendStr()
          ????fmt.Println(a("World"))
          ????fmt.Println(b("Everyone"))

          ????fmt.Println(a("Gopher"))
          ????fmt.Println(b("!"))
          }

          在 playground 上運行[13]

          在上面程序中,函數(shù) appendStr 返回了一個閉包。這個閉包綁定了變量 t。我們來理解這是什么意思。

          在第 17 行和第 18 行聲明的變量 ab 都是閉包,它們綁定了各自的 t 值。

          我們首先用參數(shù) World 調(diào)用了 a。現(xiàn)在 at 值變?yōu)榱?Hello World

          在第 20 行,我們又用參數(shù) Everyone 調(diào)用了 b。由于 b 綁定了自己的變量 t,因此 b 中的 t 還是等于初始值 Hello。于是該函數(shù)調(diào)用之后,b 中的 t 變?yōu)榱?Hello Everyone。程序的其他部分很簡單,不再解釋。

          該程序會輸出:

          Hello?World
          Hello?Everyone
          Hello?World?Gopher
          Hello?Everyone?!

          頭等函數(shù)的實際用途

          迄今為止,我們已經(jīng)定義了什么是頭等函數(shù),也看了一些專門設(shè)計的示例,來學(xué)習(xí)它們?nèi)绾喂ぷ鳌,F(xiàn)在我們來編寫一些實際的程序,來展現(xiàn)頭等函數(shù)的實際用處。

          我們會創(chuàng)建一個程序,基于一些條件,來過濾一個 students 切片。現(xiàn)在我們來逐步實現(xiàn)它。

          首先定義一個 student 類型。

          type?student?struct?{
          ????firstName?string
          ????lastName?string
          ????grade?string
          ????country?string
          }

          下一步是編寫一個 filter 函數(shù)。該函數(shù)接收一個 students 切片和一個函數(shù)作為參數(shù),這個函數(shù)會計算一個學(xué)生是否滿足篩選條件。寫出這個函數(shù)后,你很快就會明白,我們繼續(xù)吧。

          func?filter(s?[]student,?f?func(student)?bool)?[]student?{
          ????var?r?[]student
          ????for?_,?v?:=?range?s?{
          ????????if?f(v)?==?true?{
          ????????????r?=?append(r,?v)
          ????????}
          ????}
          ????return?r
          }

          在上面的函數(shù)中,filter 的第二個參數(shù)是一個函數(shù)。這個函數(shù)接收 student 參數(shù),返回一個 bool 值。這個函數(shù)計算了某一學(xué)生是否滿足篩選條件。我們在第 3 行遍歷了 student 切片,將每個學(xué)生作為參數(shù)傳遞給了函數(shù) f。如果該函數(shù)返回 true,就表示該學(xué)生通過了篩選條件,接著將該學(xué)生添加到了結(jié)果切片 r 中。你可能會很困惑這個函數(shù)的實際用途,等我們完成程序你就知道了。我添加了 main 函數(shù),整個程序如下所示:

          package?main

          import?(
          ????"fmt"
          )

          type?student?struct?{
          ????firstName?string
          ????lastName??string
          ????grade?????string
          ????country???string
          }

          func?filter(s?[]student,?f?func(student)?bool)?[]student?{
          ????var?r?[]student
          ????for?_,?v?:=?range?s?{
          ????????if?f(v)?==?true?{
          ????????????r?=?append(r,?v)
          ????????}
          ????}
          ????return?r
          }

          func?main()?{
          ????s1?:=?student{
          ????????firstName:?"Naveen",
          ????????lastName:??"Ramanathan",
          ????????grade:?????"A",
          ????????country:???"India",
          ????}
          ????s2?:=?student{
          ????????firstName:?"Samuel",
          ????????lastName:??"Johnson",
          ????????grade:?????"B",
          ????????country:???"USA",
          ????}
          ????s?:=?[]student{s1,?s2}
          ????f?:=?filter(s,?func(s?student)?bool?{
          ????????if?s.grade?==?"B"?{
          ????????????return?true
          ????????}
          ????????return?false
          ????})
          ????fmt.Println(f)
          }

          在 playground 上運行[14]

          main 函數(shù)中,我們首先創(chuàng)建了兩個學(xué)生 s1s2,并將他們添加到了切片 s。現(xiàn)在假設(shè)我們想要查詢所有成績?yōu)?B 的學(xué)生。為了實現(xiàn)這樣的功能,我們傳遞了一個檢查學(xué)生成績是否為 B 的函數(shù),如果是,該函數(shù)會返回 true。我們把這個函數(shù)作為參數(shù)傳遞給了 filter 函數(shù)(第 38 行)。上述程序會輸出:

          [{Samuel?Johnson?B?USA}]

          假設(shè)我們想要查找所有來自印度的學(xué)生。通過修改傳遞給 filter 的函數(shù)參數(shù),就很容易地實現(xiàn)了。

          實現(xiàn)它的代碼如下所示:

          c?:=?filter(s,?func(s?student)?bool?{
          ????if?s.country?==?"India"?{
          ????????return?true
          ????}
          ????return?false
          })
          fmt.Println(c)

          請將該函數(shù)添加到 main 函數(shù),并檢查它的輸出。

          我們最后再編寫一個程序,來結(jié)束這一節(jié)的討論。這個程序會對切片的每個元素執(zhí)行相同的操作,并返回結(jié)果。例如,如果我們希望將切片中的所有整數(shù)乘以 5,并返回出結(jié)果,那么通過頭等函數(shù)可以很輕松地實現(xiàn)。我們把這種對集合中的每個元素進(jìn)行操作的函數(shù)稱為 map 函數(shù)。相關(guān)代碼如下所示,它們很容易看懂。

          package?main

          import?(
          ????"fmt"
          )

          func?iMap(s?[]int,?f?func(int)?int)?[]int?{
          ????var?r?[]int
          ????for?_,?v?:=?range?s?{
          ????????r?=?append(r,?f(v))
          ????}
          ????return?r
          }
          func?main()?{
          ????a?:=?[]int{5,?6,?7,?8,?9}
          ????r?:=?iMap(a,?func(n?int)?int?{
          ????????return?n?*?5
          ????})
          ????fmt.Println(r)
          }

          在 playground 上運行[15]

          該程序會輸出:

          [25?30?35?40?45]

          現(xiàn)在簡單概括一下本教程討論的內(nèi)容:

          • 什么是頭等函數(shù)?
          • 匿名函數(shù)
          • 用戶自定義的函數(shù)類型
          • 高階函數(shù)
            • 把函數(shù)作為參數(shù),傳遞給其它函數(shù)
            • 在其它函數(shù)中返回函數(shù)
          • 閉包
          • 頭等函數(shù)的實際用途

          本教程到此結(jié)束。祝你愉快。

          上一教程 - panic 和 recover

          下一教程 - 反射[16]


          via: https://golangbot.com/first-class-functions/

          作者:Nick Coghlan[17]譯者:Noluye[18]校對:polaris1119[19]

          本文由 GCTT[20] 原創(chuàng)編譯,Go 中文網(wǎng)[21] 榮譽(yù)推出

          參考資料

          [1]

          Golang 系列教程: https://studygolang.com/subject/2

          [2]

          函數(shù): https://studygolang.com/articles/11892

          [3]

          變量: https://studygolang.com/articles/11756

          [4]

          在 playground 上運行: https://play.golang.org/p/Xm_ihamhlEv

          [5]

          在 playground 上運行: https://play.golang.org/p/c0AjB3g8UEn

          [6]

          在 playground 上運行: https://play.golang.org/p/9ttJ5Wi4fj4

          [7]

          結(jié)構(gòu)體: https://studygolang.com/articles/12263

          [8]

          在 playground 上運行: https://play.golang.org/p/n3yPQ7hG7ip

          [9]

          wiki: https://en.wikipedia.org/wiki/Higher-order_function

          [10]

          在 playground 上運行: https://play.golang.org/p/C0MNwz2TSGU

          [11]

          在 playground 上運行: https://play.golang.org/p/82y2caejUy8

          [12]

          在 playground 上運行: https://play.golang.org/p/6QriMs-zbnf

          [13]

          在 playground 上運行: https://play.golang.org/p/134NiQGPOcS

          [14]

          在 playground 上運行: https://play.golang.org/p/YUL1CqSrvfc

          [15]

          在 playground 上運行: https://play.golang.org/p/cs37QwCQ_0H

          [16]

          反射: https://studygolang.com/articles/13178

          [17]

          Nick Coghlan: https://golangbot.com/about/

          [18]

          Noluye: https://github.com/Noluye

          [19]

          polaris1119: https://github.com/polaris1119

          [20]

          GCTT: https://github.com/studygolang/GCTT

          [21]

          Go 中文網(wǎng): https://studygolang.com/



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天撸在线视频 | 夜夜夜久久久 | 99爱视频在线观看 | 污污污污污污污污污网站 | 国产免费一区二区三区在线 |