<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泛型系列:slices 包講解

          共 4804字,需瀏覽 10分鐘

           ·

          2021-11-29 18:07

          閱讀本文大概需要 10?分鐘。

          大家好,我是 polarisxu。

          前段時間,Russ Cox 明確了泛型相關(guān)的事情,原計劃在標準庫中加入泛型相關(guān)的包,改放到 golang.org/x/exp 下。

          目前,Go 泛型的主要設(shè)計者 ianlancetaylor 完成了 slices 和 maps 包的開發(fā),代碼提交到了 golang.org/x/exp 中,如果經(jīng)過使用、討論等,社區(qū)認可后,預計在 1.19 中會合入標準庫中。

          今天,通過學習 slices 包,掌握 Go 泛型的使用方法。

          01 為什么增加 slices 包

          標準庫有 bytes 和 strings 包,分別用來處理 []byte 和 string 類型,提供了眾多方便的函數(shù),但對普通的 slice,卻沒有相關(guān)的包可以使用。

          比如 bytes 和 strings 都有 Index 函數(shù),用來在 []byte 或 string 查找某個 byte 或字符串的索引。對于普通的 slice,沒法寫一大堆包來處理,只能用戶自己實現(xiàn),這也是沒有泛型的弊端。

          提供 bytes 和 strings,主要是因為它們使用頻率高

          現(xiàn)在有了泛型,可以實現(xiàn)一些便利的 slice 操作方法,必須要針對某一個具體類型的 slice 都實現(xiàn)一遍相同的功能。

          02 constraints 包

          繼續(xù)講解 slices 包之前,先看看 contraints 包。

          該包定義了一組用于類型參數(shù)(泛型)的有用約束,這個包已經(jīng)確定在 Go 1.18 標準庫中包含,截止目前(2021.11.27),該包定義了 6 個約束類型:

          //?Signed?is?a?constraint?that?permits?any?signed?integer?type.
          //?If?future?releases?of?Go?add?new?predeclared?signed?integer?types,
          //?this?constraint?will?be?modified?to?include?them.
          type?Signed?interface?{
          ?~int?|?~int8?|?~int16?|?~int32?|?~int64
          }

          //?Unsigned?is?a?constraint?that?permits?any?unsigned?integer?type.
          //?If?future?releases?of?Go?add?new?predeclared?unsigned?integer?types,
          //?this?constraint?will?be?modified?to?include?them.
          type?Unsigned?interface?{
          ?~uint?|?~uint8?|?~uint16?|?~uint32?|?~uint64?|?~uintptr
          }

          //?Integer?is?a?constraint?that?permits?any?integer?type.
          //?If?future?releases?of?Go?add?new?predeclared?integer?types,
          //?this?constraint?will?be?modified?to?include?them.
          type?Integer?interface?{
          ?Signed?|?Unsigned
          }

          //?Float?is?a?constraint?that?permits?any?floating-point?type.
          //?If?future?releases?of?Go?add?new?predeclared?floating-point?types,
          //?this?constraint?will?be?modified?to?include?them.
          type?Float?interface?{
          ?~float32?|?~float64
          }

          //?Complex?is?a?constraint?that?permits?any?complex?numeric?type.
          //?If?future?releases?of?Go?add?new?predeclared?complex?numeric?types,
          //?this?constraint?will?be?modified?to?include?them.
          type?Complex?interface?{
          ?~complex64?|?~complex128
          }

          //?Ordered?is?a?constraint?that?permits?any?ordered?type:?any?type
          //?that?supports?the?operators?=?>.
          //?If?future?releases?of?Go?add?new?ordered?types,
          //?this?constraint?will?be?modified?to?include?them.
          type?Ordered?interface?{
          ?Integer?|?Float?|?~string
          }

          前面 3 個是整型相關(guān)類型約束,F(xiàn)loat 是浮點型約束,Complex 是負數(shù)類型約束,而 Ordered 表示支持排序的類型約束,表示支持大小比較的類型。

          之前文章:《Go泛型系列:Go1.18 類型約束那些事》提到,約束語法變更了,一個是 | 符號,一個是 ~,上面定義中,很多地方都用到了 ~ 符號,它表示出了類型自身,底層類型是它的類型也適用該約束。

          03 slices 包詳解

          目前,slices 包有 14 個函數(shù),可以分成幾組:

          • slice 比較
          • 元素查找
          • 修改 slice
          • 克隆 slice

          其中,修改 slice 分為插入元素、刪除元素、連續(xù)元素去重、slice 擴容和縮容。

          slice 比較

          比較兩個 slice 中的元素,細分為是否相等和普通比較:

          func?Equal[E?comparable](s1,?s2?[]E)?bool
          func?EqualFunc[E1,?E2?any](s1?[]E1,?s2?[]E2,?eq?func(E1,?E2)?bool)?bool
          func?Compare[E?constraints.Ordered](s1,?s2?[]E)?int
          func?CompareFunc[E1,?E2?any](s1?[]E1,?s2?[]E2,?cmp?func(E1,?E2)?int)?int

          其中 comparable 約束是語言實現(xiàn)的(因為很常用),表示可比較約束(相等與否的比較)。主要,其中的 E、E1、E2 等,只是泛型類型表示,你定義時,可以用你喜歡的,比如 T、T1、T2 等。

          看一個具體的實現(xiàn):

          func?Equal[E?comparable](s1,?s2?[]E)?bool?{
          ?if?len(s1)?!=?len(s2)?{
          ??return?false
          ?}
          ?for?i,?v1?:=?range?s1?{
          ??v2?:=?s2[i]
          ??if?v1?!=?v2?{
          ???return?false
          ??}
          ?}
          ?return?true
          }

          沒有什么特別的,只不過把 s1、s2 當成同類型的 slice 進行操作而已。

          元素查找

          在 slice 中查找某個元素,分為普通的所有查找和包含判斷:

          func?Index[E?comparable](s?[]E,?v?E)?int
          func?IndexFunc[E?any](s?[]E,?f?func(E)?bool)?int
          func?Contains[E?comparable](s?[]E,?v?E)?bool

          其中,IndexFunc 的類型參數(shù)沒有使用任何約束(即用的 any),說明查找是通過 f 參數(shù)進行的,它的實現(xiàn)如下:

          func?IndexFunc[E?any](s?[]E,?f?func(E)?bool)?int?{
          ?for?i,?v?:=?range?s?{
          ??if?f(v)?{
          ???return?i
          ??}
          ?}
          ?return?-1
          }

          參數(shù) f 是一個函數(shù),它接收一個參數(shù),類型是 E,是一個泛型,和 IndexFunc 的第一個參數(shù)類型 []E 的元素類型保持一致即可,因此可以直接將遍歷 s 的元素傳遞給 f。

          修改 slice

          一般不建議做相關(guān)操作,因為性能較差。如果有較多這樣的需求,可能需要考慮更換數(shù)據(jù)結(jié)構(gòu)。

          //?往?slice?的位置?i?處插入元素(可以多個)
          func?Insert[S?~[]E,?E?any](s?S,?i?int,?v?...E)?S
          //?刪除?slice?中?i?到?j?的元素,即刪除?s[i:j]?元素
          func?Delete[S?~[]E,?E?any](s?S,?i,?j?int)?S
          //?將連續(xù)相等的元素替換為一個,類似于 Unix 的 uniq 命令。Compact 修改切片的內(nèi)容,它不會創(chuàng)建新切片
          func?Compact[S?~[]E,?E?comparable](s?S)?
          func?CompactFunc[S?~[]E,?E?any](s?S,?eq?func(E,?E)?bool)?S
          //?增加?slice?的容量,至少增加?n?個
          func?Grow[S?~[]E,?E?any](s?S,?n?int)?S
          //?移除沒有使用的容量,相當于縮容
          func?Clip[S?~[]E,?E?any](s?S)?S

          以上類型約束都包含了兩個:

          • S ~[]E:表明這是一個泛型版 slice,這是對 slice 的約束。注意 [] 前面的 ~,表明支持自定義 slice 類型,如 type myslice []int
          • E any 或 E comparable:對上面 slice 元素類型的約束。

          克隆 slice

          即獲得 slice 的副本,會進行元素拷貝,注意,slice 中元素的拷貝是淺拷貝,非值類型不會深拷貝。

          func?Clone[S?~[]E,?E?any](s?S)?S?{
          ?//?Preserve?nil?in?case?it?matters.
          ?if?s?==?nil?{
          ??return?nil
          ?}
          ?return?append(S([]E{}),?s...)
          }

          04 總結(jié)

          因為泛型的存在,同樣的功能,對不同類型的 slice 再也不用寫多份代碼。因為一些功能很常見,因此 Go 官方將其封裝,將來會在標準庫中提供。

          出于謹慎考慮,slices 包不會在 1.18 中包含,如果你需要用到 slices 中的功能,可以采用從 slices 代碼中復制的方式,個人覺得依賴 golang.org/x/exp 還是不太好。

          slices 源碼地址:https://github.com/golang/exp/blob/master/slices/slices.go。




          往期推薦


          我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標準庫》等。


          堅持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio


          瀏覽 118
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  7777精品视频 | 福利特级操比在线视频 | 日韩av天堂 | 毛片A片中文字幕在线视频 | 久久国产劲暴∨内射新川 |