英文:
Iterate over an interface
问题
我想创建一个函数,它接受一个地图或任何类型的数组,并在每个项目上调用一个函数,该函数知道如何处理遇到的任何类型。
这是我第一次失败的尝试。目前,在我的实际用例中运行时,它总是显示"uh oh!"。
func DoTheThingToAllTheThings(data_interface interface{}) int {
var numThings int
switch data := data_interface.(type) {
case map[interface{}]interface{}:
numThings = len(data)
for index, item := range data {
DoTheThing(index, item)
}
case []interface{}:
numThings = len(data)
for index, item := range data {
DoTheThing(index, item)
}
default:
fmt.Println("uh oh!")
}
return numThings
}
数组或地图可能包含许多不同的内容,因此不可能尝试匹配每个可能的输入。
换句话说,在不知道确切类型的情况下,有没有一种在Go中迭代数组或地图的方法?
英文:
I want to create a function that takes either a map or an array of whatever and iterates over it calling a function on each item which knows what to do with whatever types it encounters.
Here's my first failed attempt. Currently when I run it in my real use case it always says "uh oh!".
func DoTheThingToAllTheThings(data_interface interface{}) int {
var numThings int
switch data := data_interface.(type) {
case map[interface{}]interface{}:
numThings = len(data)
for index, item := range data {
DoTheThing(index, item)
}
case []interface{}:
numThings = len(data)
for index, item := range data {
DoTheThing(index, item)
}
default:
fmt.Println("uh oh!")
}
return numThings
}
The array or map could contain lots of different things so it isn't an option to try and match every possible input.
Stated otherwise, is there a way to iterate over an array or map in Go without knowing exactly what it is?
答案1
得分: 1
为了使你的示例工作,你需要构建一个interface{}
的数组(或映射),以便正确的类型被检测到:
// 这样是行不通的,因为.(type)会被识别为[]S
someS := []S{S{}}
DoTheThingToAllTheThings(someS)
// 这样可以:它会进入case []interface{}:
someSI := make([]interface{}, len(someS))
for i, s := range someS {
someSI[i] = s
}
DoTheThingToAllTheThings(someSI)
在你的DoTheThing
函数中,这意味着你仍然需要使用interface{}
进行操作。正如我在"What would generics in Go be?"中提到的,这里没有真正的泛型。
你可以在这里看到完整的示例。
英文:
For your example to work, you would need to build an array of interface{}
(or a map), in order for the right type to be detected:
// This won't work, as the .(type) would be []S
someS := []S{S{}}
DoTheThingToAllTheThings(someS)
// This will: it will go in case []interface{}:
someSI := make([]interface{}, len(someS))
for i, s := range someS {
someSI[i] = s
}
DoTheThingToAllTheThings(someSI)
See a full example here.
But that means you would still work with interface{}
in your DoTheThing
function.
There is no real generic here, as I mentioned in "What would generics in Go be?".
答案2
得分: 1
函数fmt.Printf("%v\n", data_interface)
正好可以实现你想要的功能。它会打印传递给它的整个映射或数组。
你可以在这里找到实现:http://golang.org/src/pkg/fmt/print.go?h=printArg#L708
printArg
末尾附近的一行是关键:
return p.printReflectValue(reflect.ValueOf(arg), verb, plus, goSyntax, depth
它使用了"reflect"包:http://golang.org/pkg/reflect/来查询参数的类型。在这里的p.printReflectValue
中:http://golang.org/src/pkg/fmt/print.go?h=printArg#L862,你将看到处理映射和结构的两种情况。然后它通过printValue
递归处理内容。
下面是一段代码,它获取一个结构的反射,然后将其转换回正确类型的另一个变量。你不能使用这种方法从一个任意类型转换为另一个任意类型,转换必须在Go语言中是合法的,而不使用反射。
package main
import (
"fmt"
"reflect"
)
type Player string
type Board struct {
Tboard [9]string
Player1 Player
Player2 Player
}
// 忽略这个函数的内容,我从其他地方复制过来的。
func makeBoard() *Board {
b := &Board{Tboard: [9]string{}}
for x := 0; x < len(b.Tboard); x++ {
b.Tboard[x] = "X"
fmt.Println(b.Tboard[x])
}
b.Player1 = "George"
b.Player2 = "Tim"
fmt.Printf("Len: %v\n", len(b.Tboard)) // => 9
fmt.Printf("Contents: %v\n", b)
fmt.Printf("Syntax: %#v\n", b)
fmt.Printf("Type: %T\n", b)
fmt.Println("Board:", b.Tboard)
return b
}
func main() {
myBoard := makeBoard()
v := reflect.ValueOf(*myBoard) // v 是 Value 类型
t := v.Type()
fmt.Printf("Value: %v %T\n", v, v)
fmt.Printf("Type: %v %T\n", t, t)
// 这里应该是一个 switch 语句
if t == reflect.TypeOf(*myBoard) {
var b2 Board
b2 = v.Interface().(Board) // 将类型 interface{} 转换为类型 Board
fmt.Printf("v converted back to: %#v\n", b2)
} else {
fmt.Printf("t is not recognized")
}
}
请注意,v
的类型是main.Board
,即完整的包名,而不是Board
。你想要进行此操作的任何结构,必须具有导出的类型才能使用反射工作。
英文:
The function fmt.Printf("%v\n", data_interface)
does exactly what you want. It will print the whole map or array passed to it.
You can find the implementation here: http://golang.org/src/pkg/fmt/print.go?h=printArg#L708
The line near the end of printArg
is key:
return p.printReflectValue(reflect.ValueOf(arg), verb, plus, goSyntax, depth
It's using the "reflect" package: http://golang.org/pkg/reflect/ to query the type of the argument. Inside p.printReflectValue
here: http://golang.org/src/pkg/fmt/print.go?h=printArg#L862 You will see the two cases where maps and structures are dealt with. It then uses recursion through printValue
to manage the contents.
Here's a bit of code that takes the reflection of a structure and then turns it back into another variable of the right type. You can't change from one arbitrary type to another using this, it has to be legal in go to cast the types from one to another without using reflection.
package main
import (
"fmt"
"reflect"
)
type Player string
type Board struct {
Tboard [9]string
Player1 Player
Player2 Player
}
// ignore this function contents, I grabbed it from elsewhere.
func makeBoard() *Board {
b := &Board{Tboard: [9]string{}}
for x := 0; x < len(b.Tboard); x++ {
b.Tboard[x] = "X"
fmt.Println(b.Tboard[x])
}
b.Player1 = "George"
b.Player2 = "Tim"
fmt.Printf("Len: %v\n", len(b.Tboard)) // => 9
fmt.Printf("Contents: %v\n", b)
fmt.Printf("Syntax: %#v\n", b)
fmt.Printf("Type: %T\n", b)
fmt.Println("Board:", b.Tboard)
return b
}
func main() {
myBoard := makeBoard()
v := reflect.ValueOf(*myBoard) // v is of type Value
t := v.Type()
fmt.Printf("Value: %v %T\n", v, v)
fmt.Printf("Type: %v %T\n", t, t)
// would be a switch
if t == reflect.TypeOf(*myBoard) {
var b2 Board
b2 = v.Interface().(Board) // cast the type interface{} into type Board
fmt.Printf("v converted back to: %#v\n", b2)
} else {
fmt.Printf("t is not recognized")
}
}
Notice that the type of v
is main.Board
, the full package name, not Board
.
Any structure you want to do this to, must have an exported type for reflection to work.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论