有没有对Go语言中数组/切片协变性缺失的明智解决方案?

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

Any sensible solution to the lack of array/slice covariance in Go?

问题

我刚刚遇到的问题是在以下情况下该怎么办:

func printItems(header string, items []interface{}, fmtString string) {
  // ...
}

func main() {
  var iarr = []int{1, 2, 3}
  var farr = []float{1.0, 2.0, 3.0}
  printItems("整数数组:", iarr, "")
  printItems("浮点数数组:", farr, "")
}

Go语言没有泛型,并且不允许使用集合协变性:

prog.go:26: 无法将iarr(类型为[]int)作为函数参数中的[]interface{}类型使用
prog.go:27: 无法将farr(类型为[]float)作为函数参数中的[]interface{}类型使用

有什么想法吗?

英文:

The problem I've just faced is what to do in the following case:

func printItems(header string, items []interface{}, fmtString string) {
  // ...
}

func main() {
  var iarr = []int{1, 2, 3}
  var farr = []float{1.0, 2.0, 3.0}
  printItems("Integer array:", iarr, "")
  printItems("Float array:", farr, "")
}

Go has no generics and doesn't allow to use collection covariance:

prog.go:26: cannot use iarr (type []int) as type []interface { } in function argument      
prog.go:27: cannot use farr (type []float) as type []interface { } in function argument

Ideas?

答案1

得分: 11

我很惊讶没有人提到使用接口来解决这个问题,这是一种非常惯用的方法,尽管有点笨重:

package main

import "fmt"

type List interface {
    At(i int) interface{}
    Len() int
}
    
func printItems(header string, items List) {
    for i := 0; i < items.Len(); i++ {
        fmt.Print(items.At(i), " ")
    }
    fmt.Println()
}

type IntList []int
type FloatList []float64

func (il IntList)   At(i int) interface{} { return il[i] }
func (fl FloatList) At(i int) interface{} { return fl[i] }

func (il IntList)   Len() int { return len(il) }
func (fl FloatList) Len() int { return len(fl) }

func main() {
    var iarr = []int{1, 2, 3}
    var farr = []float64{1.0, 2.0, 3.0}
    printItems("整数数组:", IntList(iarr))
    printItems("浮点数数组:", FloatList(farr))
}

通过为每种类型定义列表的大小和索引,您可以以“通用”的方式访问它们。当然,如果有泛型,您就不必这样做了。

英文:

I'm surprised nobody mentioned using an interface to solve the problem, which is a very idiomatic approach, if a little clunky:

package main

import &quot;fmt&quot;

type List interface {
	At(i int) interface{}
	Len() int
}
    
func printItems(header string, items List) {
	for i := 0; i &lt; items.Len(); i++ {
		fmt.Print(items.At(i), &quot; &quot;)
	}
	fmt.Println()
}

type IntList []int
type FloatList []float64

func (il IntList)   At(i int) interface{} { return il[i] }
func (fl FloatList) At(i int) interface{} { return fl[i] }

func (il IntList)   Len() int { return len(il) }
func (fl FloatList) Len() int { return len(fl) }

func main() {
	var iarr = []int{1, 2, 3}
	var farr = []float64{1.0, 2.0, 3.0}
	printItems(&quot;Integer array:&quot;, IntList(iarr))
	printItems(&quot;Float array:&quot;, FloatList(farr))
}

By defining the size and indexing of the list for each type, you can access them "generically". Of course, generics would still be nice so you don't have to do this.

答案2

得分: 7

目前没有办法在不使用以下两种方法的情况下完成这个任务:

  1. 将你的 []int[]float 都转换为 []interface{}
  2. 使 printItems 接受 interface{} 而不是 []interface{},然后使用反射,类似于 fmt 包的做法。

这两种解决方案都不太理想。

英文:

There's not really a way to do this right now without either

  1. Making your []int and []float both into []interface{}.
  2. Making printItems accept interface{} instead of []interface{} and then use reflection, similar to what the fmt package does.

Neither solution is pretty.

答案3

得分: 5

使用反射的示例:

package main

import (
    "fmt"
    "reflect"
    "strings"
    "container/vector"
)

func printItems(header string, items interface{}, fmtString string) {
    value, ok := reflect.NewValue(items).(reflect.ArrayOrSliceValue)
    if !ok {
        panic("不是数组或切片")
    }

    stringBuilder := new(vector.StringVector)
    stringBuilder.Push(header)

    n := value.Len()
    for i := 0; i < n; i++ {
        stringBuilder.Push(fmt.Sprintf(fmtString, value.Elem(i).Interface()))
    }

    fmt.Println(strings.Join(*stringBuilder, ""))
}

func main() {
    var iarr = []int{1, 2, 3}
    var farr = []float{1.0, 2.0, 3.0}

    printItems("整数数组:", iarr, " %d,")
    printItems("浮点数数组:", farr, " %.1f,")
}
英文:

An example of using reflection:

package main

import (
    &quot;fmt&quot;
    &quot;reflect&quot;
    &quot;strings&quot;
    &quot;container/vector&quot;
)

func printItems(header string, items interface{}, fmtString string) {
    value, ok := reflect.NewValue(items).(reflect.ArrayOrSliceValue)
    if !ok {
        panic(&quot;Not an array or slice&quot;)
    }

    stringBuilder := new(vector.StringVector)
    stringBuilder.Push(header)

    n := value.Len()
    for i := 0; i &lt; n; i++ {
        stringBuilder.Push(fmt.Sprintf(fmtString, value.Elem(i).Interface()))
    }

    fmt.Println(strings.Join(*stringBuilder, &quot;&quot;))
}

func main() {
    var iarr = []int{1, 2, 3}
    var farr = []float{1.0, 2.0, 3.0}

    printItems(&quot;Integer array:&quot;, iarr, &quot; %d,&quot;)
    printItems(&quot;Float array:&quot;, farr, &quot; %.1f,&quot;)
}

答案4

得分: 2

包 main

import "fmt"

func printItems(header string, items interface{}, fmtString string) {
if intItems, ok := items.([]int); ok {
fmt.Println(header, intItems)
} else if floatItems, ok := items.([]float64); ok {
fmt.Println(header, floatItems)
}
}

func main() {
var iarr = []int{1, 2, 3}
var farr = []float64{1.0, 2.0, 3.0}
printItems("整数数组:", iarr, "")
printItems("浮点数数组:", farr, "")
}

在我看来,比使用反射的解决方案更优雅。

英文:
package main

import &quot;fmt&quot;

func printItems(header string, items interface{}, fmtString string) {
  if intItems, ok := items.([]int); ok {
	fmt.Println(header, intItems)
  } else if floatItems, ok := items.([]float64); ok {
	fmt.Println(header, floatItems)
  }
}

func main() {
  var iarr = []int{1, 2, 3}
  var farr = []float64{1.0, 2.0, 3.0}
  printItems(&quot;Integer array:&quot;, iarr, &quot;&quot;)
  printItems(&quot;Float array:&quot;, farr, &quot;&quot;)
}

IMHO, more elegant then solution using reflect.

答案5

得分: 0

package main

func printItems(header string, items interface{}, fmtString string) {
// ...
}

func main() {
var iarr = []int{1, 2, 3}
var farr = []float{1.0, 2.0, 3.0}
printItems("整数数组:", iarr, "")
printItems("浮点数数组:", farr, "")
}

请查看核心Go包文档源代码中类似的函数,如fmt.Printf()。

英文:
package main

func printItems(header string, items interface{}, fmtString string) {
  // ...
}

func main() {
  var iarr = []int{1, 2, 3}
  var farr = []float{1.0, 2.0, 3.0}
  printItems(&quot;Integer array:&quot;, iarr, &quot;&quot;)
  printItems(&quot;Float array:&quot;, farr, &quot;&quot;)
}

Take a look at similar functions, like fmt.Printf(), in the core Go package documentation and source code.

huangapple
  • 本文由 发表于 2010年10月1日 20:56:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/3839335.html
匿名

发表评论

匿名网友

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

确定