doushi6864 2016-05-22 16:01
浏览 29
已采纳

如何在同一函数中接受不同类型的切片?

I have a function removeFrom that removes an item from a slice. It accepts a float64 slice and an index:

func removeFrom(slice []float64, index int) []float64 {
    if len(slice) > index {
        return append(slice[:index], slice[index+1:]...)
    }
}

It works fine, but now I have to remove from slices of integers also. So how could I change this to accept both types (and return a slice of the given type)? I tried to use empty interfaces but apparently I have to do some conversion inside the function and I didn't find out how to do it.

  • 写回答

2条回答 默认 最新

  • dpstir0081 2016-05-22 21:09
    关注

    Go doesn't support generics, there is no "common ancestor" for all slice types ([]interface{} is not "compatible" with []int for example, see Cannot convert []string to []interface {} for more details).

    So if you want your function to accept any slice types, you have to use interface{} (both for the "incoming" parameter and for the return type). But now you have an (interface) wrapper value to which you can't apply slicing and which you can't pass to the builtin append() function.

    You could use type assertion and type switches for known types, but you would have to repeat code for each, so it's not really a step ahead.

    Actually there's a way to create a removeFrom() function that will work for all slice types, with using reflection.

    reflect.Value is a type describing any Go value. It has supporting methods for different types of Go values, including slices.

    What's interesting to us is the Value.Slice() method:

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

    We can use it to slice a slice. Good. It is a key point in our element-removal algorithm. What's still needed is to "join" 2 slices, the one before and the one after the removable element. Luckily the reflect package also has support for this: reflect.AppendSlice():

    func AppendSlice(s, t Value) Value
    

    As the last remaining key, we can use Value.Len() to get the length of any slice.

    We now have everything that's needed for our general removeFrom() function, which is surprisingly simple:

    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
    }
    

    Really, that's all. Testing it:

    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)
    }
    

    Output (try it on the 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
    

    Notes:

    This solution uses reflection, so it will be slower than another solution not using reflection but having the concrete, supported types "wired in". Quick benchmarks show this general solution is 2.5 times slower than a non-reflection with wired-in types. Should be weighted whether performance or convenience/general solution is more important. Or you may combine this with concrete types: you may add a type switch to handle frequent types, and only revert to this general solution if the actual concrete type is not handled by the type switch.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥17 pro*C预编译“闪回查询”报错SCN不能识别
  • ¥15 微信会员卡接入微信支付商户号收款
  • ¥15 如何获取烟草零售终端数据
  • ¥15 数学建模招标中位数问题
  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向