成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

函數是一等公民,這到底在說什么?

開發 后端
對于有些人來說這根本不是問題,但有些人卻想不明白。我提到,在 Go 語言中,函數是一等公民,但對方不清楚這到底在說什么。看來有必要解釋下什么是一等公民。

[[378588]]

在 Go 語言中文網微信群里有人問了這么一個問題:(要加群記得在公眾號回復消息“入群”)

來自群友的問題

請問下各位大佬,這是什么語法,為什么不需要參數的?

對于有些人來說這根本不是問題,但有些人卻想不明白。我提到,在 Go 語言中,函數是一等公民,但對方不清楚這到底在說什么。看來有必要解釋下什么是一等公民。

再往下看之前,你能說出什么是一等公民嗎?

關于一等公民[1](First-class citizen)看看維基百科的定義:

In programming language design, a first-class citizen (also type, object, entity, or value) in a given programming language is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, modified, and assigned to a variable.

大意是說,在編程語言中,所謂一等公民,是指支持所有操作的實體, 這些操作通常包括作為參數傳遞,從函數返回,修改并分配給變量等。

比如 int 類型,它支持作為參數傳遞,可以從函數返回,也可以賦值給變量,因此它是一等公民。

類似的,函數是一等公民,意味著可以把函數賦值給變量或存儲在數據結構中,也可以把函數作為其它函數的參數或者返回值。關于函數是一等公民,在維基百科也有定義[2]。

In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures. Some programming language theorists require support for anonymous functions (function literals) as well.In languages with first-class functions, the names of functions do not have any special status; they are treated like ordinary variables with a function type. The term was coined by Christopher Strachey in the context of "functions as first-class citizens" in the mid-1960s.

函數作為一等公民的概念是 1960 年由英國計算機學家 Christopher Strachey[3] 提出來的。然而,并非所有語言都將函數作為一等公民,特別是早期,比如 C 語言中函數就不是一等公民,一些功能通過函數指針來實現的;再比如 C++、Java 等,都是后來的版本才加上的。

一般來說,函數式編程語言、動態語言和現代的編程語言,函數都會作為一等公民,比如:Scala、Julia 等函數式語言,JavaScript、Python 等動態語言,Go、Rust、Swift 等現代的編譯型語言。

為了讓大家對函數是一等公民有更深的理解,針對上文提到的一等公民的一等功能,我們看看 Go 語言是如何支持的。

匿名函數

函數一般是有名字的,但有時候沒有名字的函數更簡潔、好用。沒有名字的函數叫匿名函數。

以下是 Go 語言匿名函數的一個例子:

  1. package main 
  2.  
  3. import ( 
  4.  "fmt" 
  5.  
  6. func main() { 
  7.  fn := func() { 
  8.   fmt.Println("This is anonymous function!"
  9.  } 
  10.  fn() 
  11.  
  12.  fmt.Printf("The type of fn: %T\n", fn) 
  13.  
  14. // output
  15. // This is anonymous function
  16. // The type of fn: func() 

在線運行:https://play.studygolang.com/p/IcInzZsAr0a。

在 Go 中,匿名函數最常使用的場景是開啟一個 goroutine,經常會見到類似這樣的代碼:

  1. go func() { 
  2.   // xxxx 
  3. }() 

這里匿名函數定義后立即調用。此外,defer 語句中也常見。

定義函數類型

定義函數類型和其他類型類似,同時后半部分和匿名函數類似,只不過沒有函數實現。比如 net/http 包中的 HandlerFunc 函數類型:

  1. type HandlerFunc func(ResponseWriter, *Request) 

怎么使用這個類型?能看懂這樣的代碼,表示你理解了:

  1. var h http.HandlerFunc = func(w ResponseWriter, req *Request) { 
  2.   fmt.Fprintln(w, "Hello World!"

函數作為參數

意思是說,一個函數作為另一個函數的參數,也就是回調,在 JS 中很常見。在 Go 語言中也經常出現。文章開頭的問題就是函數作為參數。根據 Gin 的 API 定義,router.GET 方法的簽名如下:

  1. func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes 

其中 HandlerFunc 是一個函數類型,它的定義如下:

  1. type HandlerFunc func(*Context) 

所以,router.GET("/users", Users) 中,Users 只是 GET 函數的參數,參數類型是 HandlerFunc,而 Users 的定義只要符合 HandlerFunc 即可:

  1. func Users(ctx *gin.Context) {} 

因為這里將函數 Users 作為參數,所以自然不需要給 Users 傳遞參數,Uers 的調用有 GET 內部負責,即所謂的回調。

函數作為返回值

函數作為返回值,在 Go 中,這樣的函數一定是匿名函數。在進行 Web 開發時,中間件就會使用上函數作為返回值,還是以 Gin 為例,定義一個 Logger 中間件:

  1. func Logger() gin.HandlerFunc { 
  2.  return func(c *gin.Context) { 
  3.   t := time.Now() 
  4.  
  5.   // Set example variable 
  6.   c.Set("example""12345"
  7.  
  8.   // before request 
  9.  
  10.   c.Next() 
  11.  
  12.   // after request 
  13.   latency := time.Since(t) 
  14.   log.Print(latency) 
  15.  
  16.   // access the status we are sending 
  17.   status := c.Writer.Status() 
  18.   log.Println(status) 
  19.  } 

從上文知道,gin.HandlerFunc 是一個函數類型,因此需要返回一個該類型的實例,而匿名函數(函數字面值)只要和 gin.HandlerFunc 類型的底層類型一致,會進行隱式轉換,所以可以直接返回 func(c *gin.Context) {} 這個匿名類型。

經常聽到高階函數,函數是一等公民,就支持高階函數。一個函數只要接收一個或多個函數類型參數;亦或是返回一個函數,這樣的函數就叫做高階函數。

閉包

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

一個簡單的例子:

  1. package main 
  2.  
  3. import (   
  4.     "fmt" 
  5.  
  6. func main() {   
  7.     a := 5 
  8.     func() { 
  9.         fmt.Println("a =", a) 
  10.     }() 

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

總結

以上的知識點,可以說是學習現代編程語言必須會的。如果你還有哪個點不明白,歡迎留言交流。

最后說明一點,Go 是不支持命名函數內嵌的。即類似 JavaScript 中這樣的語法,Go 不支持:

  1. function outer() { 
  2.   console.log("In outer function"); 
  3.    
  4.   function inner() { 
  5.     console.log("In inner function"); 
  6.   } 

Go 只能通過匿名函數來實現。

參考資料

[1]一等公民: https://en.wikipedia.org/wiki/First-class_citizen

[2]維基百科也有定義: https://en.wikipedia.org/wiki/First-class_function

[3]Christopher Strachey: https://en.wikipedia.org/wiki/Christopher_Strachey

 本文轉載自微信公眾號「polarisxu」,可以通過以下二維碼關注。轉載本文請聯系polarisxu公眾號。

 

責任編輯:武曉燕 來源: polarisxu
相關推薦

2023-03-28 07:26:37

2022-03-27 23:11:39

Go語言函數

2021-11-03 07:58:27

異步編程線程

2021-03-18 08:54:55

Go 語言函數

2011-08-31 13:12:36

2015-04-27 09:48:46

Kubernetes數據中心

2014-06-25 09:11:48

技術

2022-12-08 08:40:25

大數據Hadoop存儲

2021-01-21 10:28:16

自然語言NLP人工智能

2022-09-29 09:22:33

數據倉

2022-11-07 18:12:54

Go語言函數

2019-10-23 19:30:23

AI 數據人工智能

2016-04-05 10:21:25

大數據元數據數據分析

2023-06-11 17:02:24

數字化轉型數字經濟

2019-07-17 10:10:34

Netty版本Event

2021-04-26 22:19:57

計算

2024-08-26 08:36:26

2020-09-08 17:47:36

人工智能自然語言處理

2020-11-02 12:47:56

性能優化

2020-03-09 16:43:06

腳本語言瀏覽器JavaScript
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产成人99久久亚洲综合精品 | 日韩视频免费看 | 国产玖玖| 日韩中文字幕 | 日日日色 | 精品欧美一区二区在线观看视频 | 在线成人 | 91视频在线观看 | 久久久久久国产精品 | 久久久久久久久久久高潮一区二区 | 二区三区视频 | 高清成人av | 在线观看av不卡 | 国产精品久久久久久久午夜 | www.9191| 欧美激情视频一区二区三区在线播放 | 国产精品2区 | 国产精品久久久久久久7777 | 亚洲在线一区 | 亚洲精品视频免费观看 | 亚洲视频三区 | 国产精品日本一区二区不卡视频 | 日韩一区二区三区在线 | 国产成人高清视频 | 久久久久亚洲精品 | 99riav国产一区二区三区 | 男人的天堂视频网站 | 国产高清免费视频 | 成人在线精品视频 | 啪啪网页| 九九国产在线观看 | 狠狠爱视频 | 韩国久久| 欧美一级特黄aaa大片在线观看 | 日韩精品久久久 | 国产成人精品综合 | 啪啪免费 | 精品成人 | 亚洲欧洲成人av每日更新 | 亚洲www啪成人一区二区麻豆 | 亚洲顶级毛片 |