英文:
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 "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("Integer array:", IntList(iarr))
printItems("Float array:", 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
目前没有办法在不使用以下两种方法的情况下完成这个任务:
- 将你的
[]int
和[]float
都转换为[]interface{}
。 - 使
printItems
接受interface{}
而不是[]interface{}
,然后使用反射,类似于 fmt 包的做法。
这两种解决方案都不太理想。
英文:
There's not really a way to do this right now without either
- Making your
[]int
and[]float
both into[]interface{}
. - 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 (
"fmt"
"reflect"
"strings"
"container/vector"
)
func printItems(header string, items interface{}, fmtString string) {
value, ok := reflect.NewValue(items).(reflect.ArrayOrSliceValue)
if !ok {
panic("Not an array or slice")
}
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("Integer array:", iarr, " %d,")
printItems("Float array:", farr, " %.1f,")
}
答案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 "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("Integer array:", iarr, "")
printItems("Float array:", farr, "")
}
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("Integer array:", iarr, "")
printItems("Float array:", farr, "")
}
Take a look at similar functions, like fmt.Printf(), in the core Go package documentation and source code.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论