使用函数名作为参数

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

Using function names as parameters

问题

在Go语言中,你可以像callFunction(fn func)这样将函数作为参数传递。例如:

package main

import "fmt"

func example() {
    fmt.Println("hello from example")
}

func callFunction(fn func) {
    fn()
}    

func main() {
    callFunction(example)
}

但是,当函数是结构体的成员时,是否可以调用它呢?下面的代码会失败,但是给出了一个我所说的例子:

package main

import "fmt"

type Example struct {
    x int
    y int
}

var example Example

func (e Example) StructFunction() {
    fmt.Println("hello from example")
}

func callFunction(fn func) {
    fn()
}    

func main() {
    callFunction(example.StructFunction)
}

(我知道我在这个例子中尝试做的有点奇怪。我所面临的确切问题无法很好地缩小到一个简单的例子,但这是我的问题的本质。然而,从学术角度来看,我也对此很感兴趣)

英文:

In Go, you can pass functions as parameters like callFunction(fn func). For example:

package main

import "fmt"

func example() {
    fmt.Println("hello from example")
}

func callFunction(fn func) {
    fn()
}    

func main() {
    callFunction(example)
}

But is it possible to call a function when it's a member of a struct? The following code would fail, but gives you an example of what I'm talking about:

package main

import "fmt"

type Example struct {
    x int
    y int
}

var example Example

func (e Example) StructFunction() {
    fmt.Println("hello from example")
}

func callFunction(fn func) {
    fn()
}    

func main() {
    callFunction(example.StructFunction)
}

(I know what I'm trying to do in that example is a little odd. The exact problem I have doesn't scale down to a simple example very well, but that's the essence of my problem. However I'm also intrigued about this from an academic perspective)

答案1

得分: 8

方法(不是“结构体成员”的方法,而是任何命名类型的方法,不仅限于结构体)是一等值。Go 1.0.3尚未实现方法值,但最新版本(即即将发布的Go 1.1)支持方法值。以下是完整的部分引用:

方法值

如果表达式x的静态类型为T,并且M是类型T的方法集中的方法,则x.M被称为方法值。方法值x.M是一个函数值,可以使用与调用x.M方法相同的参数进行调用。在方法值的评估过程中,表达式x被计算并保存;保存的副本随后用作任何调用中的接收器,这些调用可能在以后执行。

类型T可以是接口类型或非接口类型。

如上面对方法表达式的讨论中所述,考虑一个具有两个方法的结构体类型T,其中Mv的接收器类型为TMp的接收器类型为*T

type T struct {
    a int
}

func (tv  T) Mv(a int) int         { return 0 }  // 值接收器
func (tp *T) Mp(f float32) float32 { return 1 }  // 指针接收器

var t T
var pt *T
func makeT() T

表达式

t.Mv

产生类型为

func(int) int

这两个调用是等价的:

t.Mv(7)
f := t.Mv; f(7)

类似地,表达式

pt.Mp

产生类型为

func(float32) float32

与选择器一样,使用指针引用具有值接收器的非接口方法将自动解引用该指针:pt.Mv等同于(*pt).Mv

与方法调用一样,使用可寻址值引用具有指针接收器的非接口方法将自动获取该值的地址:t.Mv等同于(&t).Mv

f := t.Mv; f(7)   // 类似于 t.Mv(7)
f := pt.Mp; f(7)  // 类似于 pt.Mp(7)
f := pt.Mv; f(7)  // 类似于 (*pt).Mv(7)
f := t.Mp; f(7)   // 类似于 (&t).Mp(7)
f := makeT().Mp   // 无效:makeT()的结果不可寻址

尽管上面的示例使用非接口类型,但也可以从接口类型的值创建方法值。

var i interface { M(int) } = myVal
f := i.M; f(7)  // 类似于 i.M(7)
英文:

Methods (which are not "members of a struct" but methods of any named type, not only structs) are first class values. Go 1.0.3 didn't yet implemented method values but the tip version (as in the comming Go 1.1) has support method values. Quoting the full section here:

> Method values
>
> If the expression x has static type T and M is in the method set of type T, x.M is called a method value. The method value x.M is a function value that is callable with the same arguments as a method call of x.M. The expression x is evaluated and saved during the evaluation of the method value; the saved copy is then used as the receiver in any calls, which may be executed later.
>
> The type T may be an interface or non-interface type.
>
> As in the discussion of method expressions above, consider a struct type T with two methods, Mv, whose receiver is of type T, and Mp, whose receiver is of type *T.
>
> type T struct {
> a int
> }
>
> func (tv T) Mv(a int) int { return 0 } // value receiver
> func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
>
> var t T
> var pt *T
> func makeT() T
>
> The expression
>
> t.Mv
>
> yields a function value of type
>
> func(int) int
>
> These two invocations are equivalent:
>
> t.Mv(7)
> f := t.Mv; f(7)
>
> Similarly, the expression
>
> pt.Mp
>
> yields a function value of type
>
> func(float32) float32
>
> As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.
>
> As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mv is equivalent to (&t).Mv.
>
> f := t.Mv; f(7) // like t.Mv(7)
> f := pt.Mp; f(7) // like pt.Mp(7)
> f := pt.Mv; f(7) // like (*pt).Mv(7)
> f := t.Mp; f(7) // like (&t).Mp(7)
> f := makeT().Mp // invalid: result of makeT() is not addressable
>
> Although the examples above use non-interface types, it is also legal to create a method value from a value of interface type.
>
> var i interface { M(int) } = myVal
> f := i.M; f(7) // like i.M(7)

答案2

得分: 2

Go 1.0不支持将绑定方法用作函数值。它将在Go 1.1中得到支持,但在那之前,您可以通过闭包获得类似的行为。例如:

func main() {
    callFunction(func() { example.StructFunction() })
}

这不太方便,因为您最终会复制函数原型,但应该能解决问题。

英文:

Go 1.0 does not support the use of bound methods as function values. It will be supported in Go 1.1, but until then you can get similar behaviour through a closure. For example:

func main() {
    callFunction(func() { example.StructFunction() })
}

It isn't quite as convenient, since you end up duplicating the function prototype but should do the trick.

答案3

得分: 1

我修复了你的编译错误。

package main

import "fmt"

type Example struct {
    x, y float64
}

var example Example

func (e Example) StructFunction() {
    fmt.Println("hello from example")
}

func callFunction(fn func()) {
    fn()
}

func main() {
    callFunction(example.StructFunction)
}

输出:

hello from example
英文:

I fixed your compile errors.

package main

import "fmt"

type Example struct {
	x, y float64
}

var example Example

func (e Example) StructFunction() {
	fmt.Println("hello from example")
}

func callFunction(fn func()) {
	fn()
}

func main() {
	callFunction(example.StructFunction)
}

Output:

hello from example

答案4

得分: 0

为了补充@zzzz的很好的答案(以及在https://golang.org/ref/spec#Method_values给出的答案),这里有一个示例,它从接口类型的值创建了一个方法值。

package main

import "fmt"

type T struct{}

func (T) M(i int) { fmt.Println(i) }

func main() {
    myVal := T{}
    var i interface{ M(int) } = myVal
    f := i.M
    f(7) // 类似于 i.M(7)
}
英文:

To add to @zzzz great answer (and the one given at https://golang.org/ref/spec#Method_values) here is an example that creates a method value from a value of an interface type.

package main

import "fmt"

type T struct{}

func (T) M(i int) { fmt.Println(i) }

func main() {
	myVal := T{}
	var i interface{ M(int) } = myVal
	f := i.M
	f(7) // like i.M(7)
}

huangapple
  • 本文由 发表于 2013年4月9日 21:13:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/15902930.html
匿名

发表评论

匿名网友

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

确定