Golang:如何使用interface{}类型将一个值插入到切片的中间位置?

huangapple go评论79阅读模式
英文:

Golang: how to use interface{} type to insert a value into the middle of a slice?

问题

我在使用Go语言中的interface{}类型方面遇到了困难。

在这个例子中,我有一个函数,它将一个值插入到切片的中间某个位置。代码如下:

type mystruct {
    a, b, c int
}

func insert(ar []mystruct, val mystruct, i int) []mystruct {
    l := len(ar)
    if l == cap(ar) {
        tmp := make([]mystruct, l + 1, (l * 2) + 1)
        copy(tmp, ar[0:i])
        copy(tmp[i+1:], ar[i:])
        ar = tmp
    } else {
        ar = ar[0:l+1]
        copy(ar[i+1:], ar[i:])
    }
    ar[i] = val
    
    return ar
}

我希望这个函数能够接受interface{}类型的参数,以便我可以传递任意类型的切片和值,并且它能够正常执行插入操作。

根据我目前的阅读,我相信应该使用reflect包来实现这个目标。我已经阅读了有关反射和interface{}的规则以及各种反射和interface{}的教程,但我不确定如何着手解决这个问题。我希望尽量减少开销,我知道反射会使代码变慢一些,但最高效的方法是什么呢?

谢谢!

英文:

I'm having difficulty getting my head around the usage of interface{} types with Go.

In this instance I have a function which inserts a value into the middle of a slice somewhere. It looks like this:

type mystruct {
    a, b, c int
}

func insert(ar []mystruct, val mystruct, i int) []mystruct {
    l := len(ar)
    if l == cap(ar) {
        tmp := make([]mystruct, l + 1, (l * 2) + 1)
        copy(tmp, ar[0:i])
        copy(tmp[i+1:], ar[i:])
        ar = tmp
    } else {
        ar = ar[0:l+1]
        copy(ar[i+1:], ar[i:])
    }
    ar[i] = val
    
    return ar
}

I'd like for that function to be able to accept interface{} for both ar and val so that I could pass it a slice and value of any type and it would perform the insert without complaint.

From what I have read so far I believe this should be done with the reflect package. I have read the Rules of Reflection and various tutorials on reflection and interface{}, but I'm not sure how to approach this. I'd like to minimize overhead as much as possible, I understand reflection is going to slow the code down a bit but what is the most efficient way of doing this?

Thanks!

答案1

得分: 1

整个“grow if at capacity”代码是不必要的;使用内置的append函数即可完成相同的功能。

将函数签名改为:

func insert(ar interface{}, val interface{}, i int) interface{}

然后检查ar是否为切片类型,并且ar的元素类型与val的类型相同:

at, vt := reflect.TypeOf(ar), reflect.TypeOf(val)

if at.Kind() != reflect.Slice {
    panic(fmt.Sprintf("expected slice, got %T", at))
}

if at.Elem() != vt {
    panic("first argument must be a slice of the second argument's type")
}

在类型验证之后,获取一些reflect.Value

av, vv := reflect.ValueOf(ar), reflect.ValueOf(val)

然后可以使用以下方法来移动数据:

  • (reflect.Value).Index(int):访问切片的元素。
  • reflect.Copy:类似于内置的copy函数,用于在切片之间进行复制。
  • reflect.Append:用于向切片Value追加元素。
  • (reflect.Value).Set(reflect.Value):用于设置可设置的元素的值(例如从切片中获取的元素)。
  • reflect.Appendreflect.AppendSlice:类似于内置的append函数,用于向切片追加元素。

一旦得到表示结果切片的最终reflect.Value,将其作为interface{}返回,如下所示:

return result.Interface()
英文:

The whole "grow if at capactiy" code is unnecessary; use the built-in append – it does exactly that for you.

Change the signature to:

func insert(ar interface{}, val interface{}, i int) interface{}

Then check that ar is a slice and that the element type of ar is the same type as val:

at, vt := reflect.TypeOf(ar), reflect.Typeof(val)

if at.Kind() != reflect.Slice {
    panic(fmt.Sprintf("expected slice, got %T", at))
}

if at.Elem() != vt {
    panic("first argument must be a slice of the second argument's type")
}

After type validation, get a hold of some reflect.Values:

av, vv := reflect.ValueOf(ar), reflect.ValueOf(val)

And get moving stuff around using e.g:

  • (reflect.Value).Index(int) to access element of the slice.
  • reflect.Copy to copy to and from slices à la built-in copy
  • reflect.Append to append to a slice Value
  • (reflect.Value).Set(reflect.Value) to set the value of a settable element (something you got out of a slice, for instance).
  • reflect.Append or reflect.AppendSlice to append to a slice à la built-in append.

Once you have your final reflect.Value representing the result slice; return it as an interface{} like this:

return result.Interface()

huangapple
  • 本文由 发表于 2015年2月14日 16:59:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/28514046.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定