激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Golang - 淺談Go1.18中的泛型編程

淺談Go1.18中的泛型編程

2022-01-26 11:24ink19 Golang

本文主要介紹了Go1.18中的泛型編程,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

經過這幾年的千呼萬喚,簡潔的Go語言終于在1.18版本迎來泛型編程。作為一門已經有了14年歷史的強類型語言,很難相信它到現在才開始有一個正式的泛型。

淺談Go1.18中的泛型編程

以前的Go泛型

雖然直到1.18版本才加入泛型,但是在2014年便有相關的討論要在Go中加入泛型設計。但是由于各種原因沒有實現。而之后的接口(interface)的提出,讓泛型進一步擱置。但是由于接口的缺陷,最終Go團隊還是在1.18的版本中加入了泛型。實際上,這一版本的泛型設計在語言層面和接口非常相似(在實現層面肯定是不一樣的,泛型是編譯時,接口是運行時),對于他們之間的差異,也會在后面提到。

本文主要講述1.18beta1版本中的泛型,后續有改動,可能會更改文章。

泛型是什么

在我看來泛型其實用C++的模板一詞來描述就非常的準確。在寫代碼的時候,我們經常需要寫很多重復的邏輯,一般這個時候我們就會使用函數來對其進行封裝。但是由于Go是一種強類型語言,所以在定義和書寫函數的時候需要在調用前標明類型。當然如果這一重復的邏輯只需要固定的類型,這樣就足夠了,但是很多時候我們需要不同的類型進行類似的邏輯,譬如我們剛剛看到的GIF。對于普通開發人員來說這種情況可能遇到的比較少,但是在一些庫開發人員來說,這種情況變得非常的普遍。

泛型程序設計(generic programming)是程序設計語言的一種風格或范式。泛型允許程序員在強類型程序設計語言中編寫代碼時使用一些以后才指定的類型,在實例化時作為參數指明這些類型。各種程序設計語言和其編譯器、運行環境對泛型的支持均不一樣。Ada、Delphi、Eiffel、Java、C#、F#、Swift 和 Visual Basic .NET 稱之為泛型(generics);ML、Scala 和 Haskell 稱之為參數多態(parametric polymorphism);C++ 和 D稱之為模板。具有廣泛影響的1994年版的《Design Patterns》一書稱之為參數化類型(parameterized type)。

其中,C++的模版應該是做的最完善的,不僅支持簡單的模板替換,還可以處理一些簡單的邏輯,經過不斷的迭代,已經形成了一種生成代碼的編程方式,因此也叫做模板元編程(Template metaprogramming)。當然由于其和C++編程方式完全不一致,所以可讀性非常的差。而在Go的泛型設計中,為了保證泛型的簡潔,Go并不支持模版元編程(心塞,還想試試在Go里面往往騷操作呢)。

Go的泛型

接下來就是Go泛型的使用介紹了,Go支持泛型函數和泛型類型。

泛型函數

先來一個最簡單的泛型函數

?
1
2
3
func ink19FirstGen[T any](t T) {
 fmt.Println(t)
}

這是一個非常簡單的的函數,就是使用fmt.Println打印輸入的參數。相比于以前的函數,多了[T any]部分,這就是Go泛型的參數列表。

參數列表中的參數由兩部分組成,參數名和約束,其中T就是參數,any為參數的約束。從表達上來說,和Go語言一貫的風格相似,名在前,類型在后。

在Go語言中,使用接口interface做為類型的約束,其中any = interface{},即為無限制,但是以其說是無限制,倒不如說是完全限制,由于any里面沒有定義任何的方法,所以在函數里面也沒辦法調用t的任何方法。

這里有一個非常重要的問題,就是相比較于C++的模板,Go會在定義函數的時候就對函數進行解析。所以在函數中使用了的方法,一定要在約束的接口中出現。

?
1
2
3
4
5
6
7
type ink19Inf interface {
 Test()
}
 
func ink19FirstGen[T ink19Inf](t T) {
 t.Test()
}

和普通參數類似的,如果是相同的約束,參數類型也支持簡化

?
1
2
3
func ink19FirstGen[T ,T2 ink19Inf](t T, t2 []T2) {
 t.Test()
}

泛型類型

和C++中的模板類類似的,Go里面也有泛型類型,它的定義也很簡單

?
1
type ink19Vector[T any] []T

結構相比與以前的類型定義多了[T any]部分,這一部分的結構和泛型函數那一部分類似就不多介紹了。

對于泛型類型,Go也可以定義相關的方法,譬如:

?
1
2
3
4
func (m *ink19Vector[T]) Push(v T) *ink19Vector[T] {
 *m = append(*m, v)
 return m
}

在泛型結構體中,結構體也可以定義自己的類型的變量,形成鏈表

?
1
2
3
4
5
type List[T1, T2 any] struct {
 next *List[T1, T2]
 t1 T1
 t2 T2
}

PS:依據提案中的說法,第二行的參數列表應該和定義中的順序一致,以防止無限遞歸。但是在1.18beta1版本的實測中,順序不一致的寫法并不會報錯。

Go暫時不支持方法的泛型。

類型集合

雖然通過接口限制類型可以滿足絕大部分的要求,但是仍然有一些需求滿足不了,譬如運算符。假如我們有一個函數,可以傳入任意可比較的參數,然后返回較小的那一個。很自然的,我們可以寫下如下的代碼:

?
1
2
3
4
5
6
func whoismin[T any](a, b T) T {
  if a < b {
    return a
  }
  return b
}

但是,很遺憾的,由于我們對T的約束是any。所以其實來說,我們沒辦法對a和b做任何的操作,對比也是。所以在這里,我們會收到報錯

?
1
invalid operation: cannot compare a < b (operator < not defined on T)

為了解決這一問題,提案中提出了類型集合的概念。

對于一個類型,認為它代表的類型集合就是只包含這個類型的集合,即對于類型M來說,其代表的類型集合為{M}。而對于接口來說,其對應的類型集合是無限的,只要一個類型滿足接口的所有方法簽名,那么這個類型就是屬于這個接口的類型集合中。其實很容易理解類型集合就是那個識別符可以代表的類型的集合。

考慮集合的操作,對于下面這個例子

?
1
2
3
4
5
6
7
8
9
10
11
12
type ink19Inf1 interface {
 What1()
}
 
type ink19Inf2 interface {
 What2()
}
 
type ink19Inf3 interface {
 What1()
 What2()
}

假設ink19Inf1的類型集合為A,ink19Inf2的為B,ink19Inf3的為C。那么很容得到C=A?B。即C為A和B的交集。當然只有交集是不行的,后面還有說明實現并集。

為了進一步的說明類型集合,我們先來回憶一下接口的定義,對于之前的接口來說,接口的元素一共有兩種:方法簽名和其他接口。

?
1
2
3
4
5
6
7
8
type ink19Inf1 interface {
 What()
}
 
type ink19Inf2 interface {
 ink19Inf1
 It()
}

比如ink19Inf2中的第一個元素就是其他接口,第二個元素是其他簽名。但是僅僅只是有這兩種元素,對于泛型約束來說是完全不夠的。為此,提案中加入了另外三種不同的元素,需要注意的是,如果一個接口加入了這額外三種元素,那么這個接口就不能再作為普通的接口使用了,只能用作泛型。

第一個增加的是類型元素。以前的接口是不能用類型作為接口的,但是在作為約束中可以這樣操作。作為元素的時候就是提供了一個只包含自己本身的類型作為元素的類型集合。

第二個是增加了近似約束元素,寫法是在類型前面增加~符號,如

?
1
2
3
type ink19Inf1 interface {
 ~int
}

這一個元素的意義是為接口提供了一個所有以int為底層類型的集合。所以被~修飾的類型也應該是一個底層類型,不然提供的集合就是空集,沒有任何意義。具體的區別可以看下面的這個例子。

?
1
2
3
4
5
6
7
8
9
type ink19Inf3 interface {
 int
}
 
type ink19Inf4 interface {
 ~int
}
 
type MyInt int

首先我們定義了兩個接口,第一個接口使用的是額外的第一種元素, 因此它的類型集合只包含了int。另一個使用了第二種元素,它的類型集合包含了所有以int為底層類型的類型。然后我們定義了一個MyInt類型,它是以int為底層類型的類型。需要注意的是,在Go中MyInt和int是兩種不同的類型。最后我們寫兩個方法來分別使用兩個接口為約束。

?
1
2
3
4
5
6
7
8
9
10
11
func ink19Print1[T ink19Inf3](t T) {
 fmt.Println(t)
}
 
func ink19Print2[T ink19Inf4](t T) {
 fmt.Println(t)
}
 
var data MyInt = 1
ink19Print1(data)  // 錯誤
ink19Print2(data)

第三個元素是聯合約束。使用方法如下

?
1
2
3
type ink19Inf5 interface {
 int | float32 | bool | ~string | ink19Inf3
}

使用方法非常簡單,就是將并集的元素一個一個使用|連接就就好了。需要注意的是聯合約束的元素只支持類型,近視約束和其他只包含以上三種額外元素的接口(即,不支持包含方法簽名的接口)。

回到之前的問題,對于需要使用操作符的情況,有了以上的工具后就可以解決了。

縱觀整個Go語言,由于并不支持操作符,所以有操作符(除了==和!=)的其實只有有限的幾種類型,譬如:int,float32,string等等。

所以對于需要使用比較運算符的約束的時候,可以使用如下的一個約束接口:

?
1
2
3
4
5
6
type Ordered interface {
 ~int | ~int8 | ~int16 | ~int32 | ~int64 |
  ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
  ~float32 | ~float64 |
  ~string
}

為了方便使用,Go標準庫里面提供了一個constraints來提供相關的約束。

上面提到,對于除了==和!=以外的操作符可以通過對所有的類型進行枚舉來實現。但是對于這兩個操作符,用戶自定義的類型也會有這兩個操作符,沒辦法枚舉實現。官方給出的方法是通過使用一個一個內建的約束comparable來完成操作。譬如

?
1
2
3
func IsSame[T comparable](a T, b T) bool {
 return a == b
}

和接口的差異

由于本人對于Go的接口使用并不多,所以如果有不足的地方請及時指正。

  • 實現方法上,泛型是編譯時,接口是運行時;
  • 可以實現操作符的約束;
  • 返回的參數可以是特定的類型,而接口只能返回固定的接口類型;
  • 相比較于接口,泛型的約束可以有更多的操作。

總結

以上就是Go語言泛型的使用。總的來說,比較完整,實現了大部分的功能,相比于接口,有一定的差異。從體驗上來說有較高的提升,但是其缺點也非常的多。首先是其后面提出的三種元素,它將接口和類型限制隔離開了,這是一個特別奇葩的操作,感覺不符合Go語言的簡潔實現。添加的三種元素中,我們主要來看第三種,聯合。代碼在分析的時候會對每一個元素測試,看看能不能通過編譯。所以從集合的角度上來看,如果我們把一個類型可以進行的操作可做是一個集合,那么這一個聯合就是在一個限定的類型集合里面(枚舉出的)對每一個類型的操作集合進行一個交集操作。回到原來,其實出現這個語法特性的最大原因就是Go語言不支持操作符重載,所以沒辦法對操作符進行枚舉,那為什么不直接在這個版本實現操作符重載呢?或者直接不考慮這一部分,讓傳入的結構體只能使用方法,不能使用操作符。并且,即使加入了這三種元素,還是有兩種操作符!=和==無法使用現在有的實現,只能使用一個內建的符號來代表這一類的方法,個人感覺非常丑陋。

到此這篇關于淺談Go1.18中的泛型編程的文章就介紹到這了,更多相關Go  泛型編程內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/ink19/p/go_generic_programming.html

延伸 · 閱讀

精彩推薦
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細介紹,從例子說起,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網11352020-05-21
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

    這篇文章主要介紹了Golang通脈之數據類型,在編程語言中標識符就是定義的具有某種意義的詞,比如變量名、常量名、函數名等等,Go語言中標識符允許由...

    4272021-11-24
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

    這篇文章主要給大家介紹了關于golang的httpserver優雅重啟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    helight2992020-05-14
  • Golanggo日志系統logrus顯示文件和行號的操作

    go日志系統logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

    這篇文章主要介紹了Golang中Bit數組的實現方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨尊11682021-06-09
主站蜘蛛池模板: 免费毛片视频 | 国产拍拍拍三级费视频在线观看 | 国产1区2区3区在线观看 | 特级黄色小说 | 精品国产一区二区三区天美传媒 | 一级看片免费视频 | 亚洲视频成人在线 | 亚洲精品久久久久久久久久久 | 中国美女一级黄色片 | 香蕉成人在线视频 | 日韩色视频 | 成人免费在线视频播放 | 日本在线播放一区二区 | 亚洲xxx在线观看 | 中文字幕在线播放第一页 | 亚洲精品一区国产精品丝瓜 | 中文字幕专区高清在线观看 | 免费观看一级 | 国产一区二区久久精品 | 热99在线视频 | 国产人成免费爽爽爽视频 | 欧美成人a | 日韩视频中文 | 久草在线资源观看 | :国产精品成人一区二区三区 | 日韩视频www | 免费黄色一级 | 黑人一区二区 | 性欧美大战久久久久久久免费观看 | 日韩黄色免费在线观看 | 欧美成人性生活 | 水卜樱一区二区av | 狠狠色噜噜狠狠狠米奇9999 | sm高h视频 | 久久精品日产第一区二区三区 | 日本网站在线看 | lutube成人福利在线观看污 | 亚洲午夜免费电影 | 欧美精品一区二区久久 | 精品国产乱码久久久久久丨区2区 | 中文国产在线视频 |