对一组项目执行一个动作

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

Perform an action on a collection of items

问题

在我的实际代码中,我正在使用encoding/xml解析XML文档,并且我基本上有一堆嵌套的结构,形式如下 - 所有这些结构都可能出现多次,除了顶层的statements元素:

statements
  statement
    opcode
      args
        pre
        post

我对Go相当新手,显然我对interface{}(空接口)的工作原理有误解:

.\stmtgen.go:58: 无法将print_name(类型为func(Statement))作为performAction的参数中类型为func(interface {})的类型使用
.\stmtgen.go:58: 无法将slist(类型为[]Statement)作为performAction的参数中类型为[]interface {}的类型使用

相关示例代码:

<!-- language: lang-golang -->

package main
import "fmt"

// 实际上是我的代码中的一个结构,但这足以进行演示。
type Opcode int

// 一个语句有一个名称,多个操作码可以使用此名称。
type Statement struct {
    Name    string
    Opcodes []Opcode
}

// 打印语句名称。
func print_name(stmt Statement) {
    fmt.Println(stmt.Name)
}

// 对集合中的每个项执行操作。
func performAction(action func(interface{}), v []interface{}) {
    for i := range v {
        action(v[i])
    }
}

func main() {
    slist := make([]Statement, 3)
    slist[0] = Statement{"Statement 1"}
    slist[1] = Statement{"Statement 2"}
    slist[2] = Statement{"Statement 3"}

    // 错误在这里
    performAction(print_name, slist)
}

我必须为每种类型创建函数来打印值吗?

英文:

In my actual code, I'm parsing an XML document using encoding/xml, and I basically have a bunch of nested structures of the following form &mdash; all of which may occur multiple times, except the top-level statements element:

statements
  statement
    opcode
      args
        pre
        post

I'm fairly new to Go, and I'm clearly misunderstanding how interface{} (the empty interface) works:

.\stmtgen.go:58: cannot use print_name (type func(Statement)) as type func(interface {}) in argument to performAction
.\stmtgen.go:58: cannot use slist (type []Statement) as type []interface {} in argument to performAction

Relevant example code:

<!-- language: lang-golang -->

package main
import &quot;fmt&quot;

// Actually a structure in my code, but this suffices for demonstration.
type Opcode int

// A Statement has a Name and multiple Opcodes may use this Name.
type Statement struct {
    Name    string
    Opcodes []Opcode
}

// Print the statement name.
func print_name(stmt Statement) {
    fmt.Println(stmt.Name)
}

// Perform an action on each item of a collection.
func performAction(action func(interface{}), v []interface{}) {
    for i := range v {
        action(v[i])
    }
}

func main() {
    slist := make([]Statement, 3)
    slist[0] = Statement{&quot;Statement 1&quot;}
    slist[1] = Statement{&quot;Statement 2&quot;}
    slist[2] = Statement{&quot;Statement 3&quot;}

    //ERROR HERE
    performAction(print_name, slist)
}

Must I create functions to print the values for every single type?

答案1

得分: 1

一个空的interface{}可以包含任何值,并作为类型interface{}传递。当你需要从中获取值时,可以执行类型断言,例如:

var anyValue interface{}
anyValue = "hello"

strValue := anyValue.(string)

如果anyValue不是所断言的类型,则会引发恐慌。

类型断言还可以用于返回一个布尔值,以判断接口是否为该类型:

strValue, ok := anyValue.(string)
if ok {
    // anyValue 包含一个字符串!
}

如果你不知道接口的类型,可以使用switch语句来确定其类型,例如:

switch val := anyValue.(type) {
case string:
    // anyValue 包含一个字符串
    // val 是一个字符串
    break
case int:
    // anyValue 包含一个整数
    // val 是一个整数
    break
default:
    // 未处理的接口类型
}

希望这样可以更清楚地理解空接口interface{}的类型。

而声明了方法的接口interface{...}是不同的,它们不能有成员(像结构体那样),只能有方法,并且它们的底层类型必须实现接口中声明的所有方法。你可以有一个名为actionPerformer的接口(接口名称应该以“er”作为后缀,因为它们正在做某些事情):

type actionPerformer interface {
    action(interface{})
}

实现了接口中所有方法的类型可以转换为该接口类型,然后如果在接口上调用其中一个方法,它将在底层类型上运行该方法。例如,如果Statement结构体实现了action(interface{})方法,那么Statement结构体可以转换为actionPerformer类型,如果在actionPerformer上调用action(interface{})函数,将运行Statement结构体上的action函数。因此,你可以有多个类型都有action(interface{})方法,并且它们都可以转换为actionPerformer,你可以在其上调用action函数。

func (code Opcode) action(arg interface{}) {
    fmt.Println(arg.(int) + int(code))
}

func (stmt Statement) action(arg interface{}) {
    fmt.Println(arg.(string), stmt.Name)
}

stmt := Statement{"Statement 1", nil}
stmtActionPerformer := actionPerformer(stmt)

opcode := Opcode(5)
opcodeActionPerformer := actionPerformer(opcode)

stmtActionPerformer.action("hello") // 将打印 "hello" + 语句的名称
opcodeActionPerformer.action(2) // 将打印 7

类型断言仍然可以用于这些类型的接口,例如:

stmt := stmtActionPerformer.(Statement)
fmt.Println(stmt.Name)

这只是一个人为的例子,但是有了这个理解,你可能希望像这样使用接口来编写代码。请记住,在接口之间进行转换是昂贵的,因此应该谨慎使用,但是当使用正确时,它们是强大的工具。

对于你的例子,一个简单的printNames函数比所有的接口转换更高效(请注意,在Go中,名称应该使用驼峰格式,而不是使用下划线):

func printNames(stmts []Statement) {
    for _, stmt := range stmts {
        fmt.Println(stmt.Name)
    }
}

还可以定义一个StatementList类型,并为其添加方法:

type StatementList []Statement

func (list StatementList) printNames() {
    for _, stmt := range list {
        fmt.Println(stmt.Name)
    }
}

掌握这些知识后,使用Go语言会更有趣,希望对你有所帮助 对一组项目执行一个动作

英文:

An empty interface{} can contain any value and passed around as the type interface{}. when you need the value from it, you can perform a type assertion like this:

var anyValue interface{}
anyValue = &quot;hello&quot;

strValue := anyValue.(string) 

If anyValue is not of the type being asserted then it will cause a panic

the type assertion can also be used to return a bool if the interface is of that type with a multiple return

strValue, ok := anyValue.(string)
if ok {
    //anyValue contains a string!
}

If you dont know the type of the interface, you can use a switch to determine it's type like this:

switch val := anyValue.(type) {
case string:
    // anyValue contains a string
    // and val is a string
    break
case int:
    // anyValue contains an int
    // and val is and int
    break
default:
    //unhandled interface type
}

Hopefully this makes the empty interface{} type clearer.

interfaces{...} which have methods declared in them are different, they can not have members (like structs can), only methods, and their underlying type must implement all the methods declared in the interface. You could have an interface actionPerformer (interface names should have the suffix "er" as they are doing something)

type actionPerformer interface {
    action(interface{})
}

A type that implements all the methods in an interface can be cast to that interface type, then if you call one of those methods on the interface, it will run the method on the underlying type.
For example, if the Statement struct implements the action(interface{}) method, the Statement struct can be cast to an actionPerformer type and if the action(interface{}) function is called on the actionPerformer, the action function on the Statement struct is run. So you could have multiple types that all have the action(interface{}) method and they can all be cast to an actionPerformer which you can call the action function on.

func (code Opcode) action(arg interface{}) {
    fmt.Println(arg.(int) + int(code))
}

func (stmt Statement) action(arg interface{}) {
    fmt.Println(arg.(string), stmt.Name)
}

stmt := Statement{&quot;Statement 1&quot;, nil}
stmtActionPerformer := actionPerformer(stmt)

opcode := Opcode(5)
opcodeActionPerformer := actionPerformer(opcode)

stmtActionPerformer.action(&quot;hello&quot;) // will print &quot;hello &quot;+whatever the statements name is
opcodeActionPerformer.action(2) //will print be 7

Type assertions can still be used on these types of interface e.g.

stmt := stmtActionPerformer.(Statement)
fmt.Println(stmt.Name)

This is a contrived example, but with this in mind, you might want to write your code using interfaces like this.
Remember casting between interfaces is costly, so should be done sparingly, however they are a powerful tool when used correctly.

For your example, a simple printNames function would be much more efficient than all that interface casting (note that in golang, names should be in the CamelCase format, not using underscores)

func printNames(stmts []Statement) {
    for _, stmt := range stmts {
	    fmt.Println(stmt.Name)
    }
}

It might also be useful to have a type StatementList and add methods to it:

type StatementList []Statement

func (list StatementList) printNames() {
    for _, stmt := range list {
        fmt.Println(stmt.Name)
    }
}

Getting the hang of this stuff make golang a lot more fun, hope this helps 对一组项目执行一个动作

答案2

得分: 0

你必须像参数类型一样精确声明performAction函数的参数。

func performAction(action func(Statement), v []Statement) {
    for i := range v {
        action(v[i])
    }
}

或者你可以在所有参数上使用interface{},然后根据需要进行类型转换。

func performAction(action interface{}, v interface{}) {
    for _, each := range v.([]Statement) {
        action.(func(Statement))(each)
    }
}
  • 类型为[]Statement的数据不能赋值给[]interface{}
  • 类型为func(Statement)的数据也不能赋值给func(interface{})

使用interface{},然后将其转换为原始类型。

英文:

You have to declare the parameters of performAction exactly same like the arguments type.

func performAction(action func(Statement), v []Statement) {
    for i := range v {
        action(v[i])
    }
}

Or you could use interface{} on all parameters instead. Then cast it according to the needs.

func performAction(action interface{}, v interface{}) {
    for _, each := range v.([]Statement) {
        action.(func(Statement))(each)
    }
}
  • data with type []Statement cannot be assigned to []interface{}
  • also for type func(Statement) cannot be assigned to func(interface{})

Use interface{}, then cast it to the original type.

答案3

得分: 0

这对我有效:

package main

import (
	"fmt"
)

// 实际上是我代码中的一个结构体,但这足以进行演示。
type Opcode int

// 一个语句有一个名称,多个操作码可以使用这个名称。
type Statement struct {
	Name    string
	Opcodes []Opcode
}

// 打印语句的名称。
func print_name(stmt interface{}) {
	if s, ok := stmt.(Statement); !ok {
		panic("typ err")
	} else {
		fmt.Println(s.Name)
	}
}

// 对集合中的每个项执行操作。
func performAction(action func(interface{}), v []interface{}) {
	for i := range v {
		action(v[i])
	}
}

func main() {
	slist := make([]interface{}, 3)
	slist[0] = Statement{"语句 1", nil}
	slist[1] = Statement{"语句 2", nil}
	slist[2] = Statement{"语句 3", nil}

	performAction(print_name, slist)
	/*输出:
	语句 1
	语句 2
	语句 3
	*/
}

希望对你有帮助!

英文:

this works for me:

package main

import (
	&quot;fmt&quot;
)

// Actually a structure in my code, but this suffices for demonstration.
type Opcode int

// A Statement has a Name and multiple Opcodes may use this Name.
type Statement struct {
	Name    string
	Opcodes []Opcode
}

// Print the statement name.
func print_name(stmt interface{}) {
	if s, ok := stmt.(Statement); !ok {
		panic(&quot;typ err&quot;)
	} else {
		fmt.Println(s.Name)
	}
}

// Perform an action on each item of a collection.
func performAction(action func(interface{}), v []interface{}) {
	for i := range v {
		action(v[i])
	}
}

func main() {
	slist := make([]interface{}, 3)
	slist[0] = Statement{&quot;Statement 1&quot;, nil}
	slist[1] = Statement{&quot;Statement 2&quot;, nil}
	slist[2] = Statement{&quot;Statement 3&quot;, nil}

	performAction(print_name, slist)
	/*output:
	Statement 1
	Statement 2
	Statement 3
	*/
}

huangapple
  • 本文由 发表于 2016年4月27日 12:29:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/36880335.html
匿名

发表评论

匿名网友

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

确定