在Go语言中,将任意长度的切片转换为数组是可能的吗?

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

Is it possible to convert arbitrary length slices to arrays in golang?

问题

基本上,我需要禁止重复值的接收数据,这些数据类型各不相同(例如int8(1),然后uint32(1)或big.Int(1)是无效的),可以按照以下方式实现:

var m map[interface{}]bool

func tryAddValue(value interface{}) {
	switch v := value.(type) {
    // ... 省略其他类型以保持简洁
	case *big.Int:
		if v.IsUint64() {
			value = v.Uint64()
		} else if v.IsInt64() {
			value = v.Int64()
		} else {
            bits := v.Bits()
			value = [len(bits)]big.Word(bits)
            //       ^^^ 这段代码无法编译,因为它不是常量
		}
	}
	if m[value] {
		panic(fmt.Errorf("value %v already exists", value))
	}
	m[value] = true
}

由于切片不能作为映射键,对于big.Int,我唯一的其他选择是字符串转换和数组转换,而字符串转换是昂贵的(此代码将在热路径上执行)。

英文:

Essentially, I need to disallow duplicate values for received data of varying types (i.e. int8(1), then uint32(1) or big.Int(1) is invalid), along the lines of:

var m map[interface{}]bool

func tryAddValue(value interface{}) {
	switch v := value.(type) {
    // ... other types omitted for brevity
	case *big.Int:
		if v.IsUint64() {
			value = v.Uint64()
		} else if v.IsInt64() {
			value = v.Int64()
		} else {
            bits := v.Bits()
			value = [len(bits)]big.Word(bits)
            //       ^^^ This doesn't compile because it's not a constant
		}
	}
	if m[value] {
		panic(fmt.Errorf("value %v already exists", value))
	}
	m[value] = true
}

Since slices cannot be map keys, my only other options for big.Int are string conversion and array conversion, and string conversion is expensive (this code will be on a hot path).

答案1

得分: 0

啊,反射来拯救:

	bits := v.Bits()
	t := reflect.ArrayOf(len(bits), reflect.TypeOf(bits[0]))
	arr := reflect.New(t).Elem()
	slice := arr.Slice(0, len(bits)).Interface()
	copy(slice.([]big.Word), bits)
	value = arr.Interface()

只使用整数类型的基本实现 https://go.dev/play/p/vOS4exXRkEt

注意:除非你真的需要一个实际的数组,否则有更快的方法来创建映射键。请参阅注释。

注意2:上述代码没有考虑符号。要包含符号信息,请手动将其编码为第一个字:

	bits := v.Bits()
	arrLen := len(bits) + 1
	t := reflect.ArrayOf(arrLen, reflect.TypeOf(bits[0]))
	arr := reflect.New(t).Elem()
	slice := arr.Slice(0, arrLen).Interface().([]big.Word)
	slice[0] = big.Word(v.Sign())
	copy(slice[1:], bits)
	value = arr.Interface()

https://go.dev/play/p/lKJQJJSENw7

英文:

Ah, reflect to the rescue:

	bits := v.Bits()
	t := reflect.ArrayOf(len(bits), reflect.TypeOf(bits[0]))
	arr := reflect.New(t).Elem()
	slice := arr.Slice(0, len(bits)).Interface()
	copy(slice.([]big.Word), bits)
	value = arr.Interface()

Basic implementation with only integer types https://go.dev/play/p/vOS4exXRkEt

Note: Unless you actually need an actual array, there are faster ways to make map keys. See the comments.

Note2: The above code doesn't take into account the sign. To include sign info, manually encode it as the first word:

	bits := v.Bits()
	arrLen := len(bits) + 1
	t := reflect.ArrayOf(arrLen, reflect.TypeOf(bits[0]))
	arr := reflect.New(t).Elem()
	slice := arr.Slice(0, arrLen).Interface().([]big.Word)
	slice[0] = big.Word(v.Sign())
	copy(slice[1:], bits)
	value = arr.Interface()

https://go.dev/play/p/lKJQJJSENw7

huangapple
  • 本文由 发表于 2023年7月5日 19:19:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76619816.html
匿名

发表评论

匿名网友

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

确定