亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何在同一個函數中接受不同類型的切片?

如何在同一個函數中接受不同類型的切片?

Go
慕萊塢森 2022-01-10 18:49:10
我有一個removeFrom從切片中刪除項目的功能。它接受一個float64切片和一個索引:func removeFrom(slice []float64, index int) []float64 {    if len(slice) > index {        return append(slice[:index], slice[index+1:]...)    }}它工作正常,但現在我也必須從整數切片中刪除。那么我怎么能改變它以接受這兩種類型(并返回給定類型的切片)?我嘗試使用空接口,但顯然我必須在函數內部進行一些轉換,但我不知道該怎么做。
查看完整描述

2 回答

?
四季花海

TA貢獻1811條經驗 獲得超5個贊

簡短的回答?你不能。


長答案,你仍然不能直接這樣做,但是:


func removeFrom(slice interface{}, index int) interface{} {

    switch slice := slice.(type) {

    case []float64:

        if len(slice) > index {

            return append(slice[:index], slice[index+1:]...)

        }

    case []int64:

        if len(slice) > index {

            return append(slice[:index], slice[index+1:]...)

        }

    case []int:

        if len(slice) > index {

            return append(slice[:index], slice[index+1:]...)

        }

    default:

        log.Panicf("unknown type: %T", slice)

    }

}


查看完整回答
反對 回復 2022-01-10
?
catspeake

TA貢獻1111條經驗 獲得超0個贊

Go 不支持泛型,所有切片類型都沒有“共同祖先”(例如,[]interface{}與“不兼容” []int,請參閱無法將 []string 轉換為 []interface {}了解更多詳細信息)。


因此,如果您希望您的函數接受任何切片類型,則必須使用interface{}(對于“傳入”參數和返回類型)。但是現在您有一個(接口)包裝器值,您不能對其應用切片,也不能將其傳遞給內置append()函數。


您可以對已知類型使用類型斷言和類型開關,但是您必須為每個類型重復代碼,所以這并不是真正的領先一步。


實際上,有一種方法可以removeFrom()使用反射創建一個適用于所有切片類型的函數。


reflect.Value是一種描述任何 Go 值的類型。它為不同類型的 Go 值提供支持方法,包括切片。


我們感興趣的是Value.Slice()方法:


func (v Value) Slice(i, j int) Value

我們可以用它來切片。好的。這是我們的元素去除算法中的一個關鍵點。仍然需要“加入”2 個切片,即可移動元素之前的切片和可移動元素之后的切片。幸運的是,這個reflect包也支持這個reflect.AppendSlice()::


func AppendSlice(s, t Value) Value

作為最后剩下的鍵,我們可以Value.Len()用來獲取任何切片的長度。


我們現在擁有了我們的一般removeFrom()功能所需的一切,這非常簡單:


func removeFrom(s interface{}, idx int) interface{} {

    if v := reflect.ValueOf(s); v.Len() > idx {

        return reflect.AppendSlice(v.Slice(0, idx), v.Slice(idx+1, v.Len())).Interface()

    }

    return s

}

真的,僅此而已。測試它:


for i := 0; i < 4; i++ {

    fmt.Println(removeFrom([]int{0, 1, 2}, i), "missing:", i)

}

for i := 0; i < 4; i++ {

    fmt.Println(removeFrom([]string{"zero", "one", "two"}, i), "missing:", i)

}

輸出(在Go Playground上試試):


[1 2] missing: 0

[0 2] missing: 1

[0 1] missing: 2

[0 1 2] missing: 3

[one two] missing: 0

[zero two] missing: 1

[zero one] missing: 2

[zero one two] missing: 3

筆記:


該解決方案使用反射,因此它比不使用反射但具有“連接”的具體支持類型的另一個解決方案要慢。快速基準測試表明,這種通用解決方案比有線輸入類型的非反射慢 2.5 倍。應該權衡性能或便利性/通用解決方案是否更重要?;蛘吣憧梢詫⑺c具體類型結合起來:你可以添加一個類型開關來處理頻繁的類型,并且只有在類型開關沒有處理實際的具體類型時才恢復到這個通用解決方案。


查看完整回答
反對 回復 2022-01-10
  • 2 回答
  • 0 關注
  • 146 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號