【golang】實現數組 map 方法
經常會在項目裏用到數組的 map
方法,閒來無事打算在 golang
中實現一下 map 方法。上工具要先上用法
numbers := []int{1, 2, 3}
// convert to []interface{}
interfaceNumbers, err := utils.ToInterface(numbers)
utils.Check(err)
dup := utils.Map(interfaceNumbers, func(item interface{}) interface{} {
v, ok := item.(int)
if !ok {
utils.Check(fmt.Errorf("assert error"))
}
return v + 1
})
fmt.Println(dup) // [ 2, 3, 4 ]
其實跟其他支持函數式的語言無異,只是多了個類型斷言,這是有點麻煩也迫不得已的地方,先別急,實現會放到下面。
首先要捋清思路的是,map
的第一個參數是傳遞目標切片,當然切片可以是任意類型的切片,但是有點頭疼的是 Golang
中並沒有泛型,所以這裏只能用 []interface{}
,這樣導致的結果是傳遞參數之前是有確切類型聲明的,但是出來的時候就是 []interface{}
還得在外面再做一層斷言。
先來看一下 map
函數的具體實現
func Map(slice []interface{}, cb func(interface{}) interface{}) []interface{} {
dup := make([]interface{}, len(slice))
copy(dup, slice)
for index, val := range slice {
dup[index] = cb(val)
}
return dup
}
實現比較簡單,只是將傳遞進來的回調在內部執行傳遞切片的每個項,這裏爲了避免副作用,在內部重新創建了一個新的切片,通過 copy
方法把舊切片的值拷貝到新的切片當中。
重要的是 map
的函數簽名,傳遞一個 slice
和 cb
返回一個 []interface{}
,值得關注的是 slice
的類型,slice
的類型爲 []interface{}
,看起來是沒有問題的,但是如果你傳遞一個其他的類型的切片就會拋出 panic
,這是因爲 golang
會認爲它們是兩種不同的類型,儘管 interface{}
是可以兼容任何類型,但是 []interface{}
是一種新的類型,它是一種能夠容納任何類型的切片,它並不兼容任何類型的切片,詳情戳
這裏就不再贅述了。
所以我們還要實現一個能夠把任意切片轉換爲 []interface{}
的一個轉換函數,思路是利用反射判斷類型,然後將它 copy 成一個新的切片並返回
func ToInterface(slice interface{}) ([]interface{}, error) {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return nil, fmt.Errorf("argument must be a slice")
}
r := make([]interface{}, v.Len())
for i := 0; i < v.Len(); i++ {
r[i] = v.Index(i).Interface()
}
return r, nil
}
ToInterface
利用反射檢測類型是否正確,如果不是切片,則拋出一個 error。