将一个结构体方法作为参数传递

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

Passing a struct method as an argument

问题

我不确定我是否试图超出Go允许的限制,但这是上下文:
我有一个名为Map的类型,其中包含大量有用的成员函数,例如InterpolateAddClear。我还有一个名为Layered的结构体类型,其中包含多个Map

type Map [][]float64

func (m Map) Interpolate(min, max float64) { ... }
func (m Map) Add(val float64) { ... }
func (m Map) Clear() { ... }

type Layered struct {
    data []Map
}

我想在Layered中添加一个成员函数,该函数接受Map的任何成员函数作为参数,然后在所有包含的Map上调用它。大致如下:

func (l Layered) MapCall(callMe func(Map)) {
    for _, m := range l.data {
        callMe(m)
    }
}

这部分可以工作,但仅适用于Map的不带额外参数的成员函数,例如Clear。是否有可能接受Map任何成员函数作为参数(以及以...interface{}形式的潜在参数),然后在Layered的所有成员Map上调用该函数,带有其正确的参数?例如,像这样:

func (l Layered) MapCall(callMe func(Map, ...interface{}), params ...interface{}) {
    for _, m := range l.data {
        callMe(m, params...)
    }
}
英文:

I am not sure if I'm trying to exceed the limits of what Go will allow, but here is the context:
I have type called Map with a hefty number of helpful member functions, such as Interpolate, Add, and Clear. I also have a struct type, Layered, that contains several Maps.

type Map [][]float64

func (m Map) Interpolate(min, max float64) { ... }
func (m Map) Add(val float64) { ... }
func (m Map) Clear() { ... }

type Layered struct {
    data []Map
}

I would like to add a member function to Layered that accepts any member function of Map as an argument, and then calls it on all of its contained Maps. Something along the lines of:

func (l Layered) MapCall(callMe func(Map)) {
    for _, m := range l.data {
        callMe(m)
    }
}

This partially works, but only for member functions of Map that take no additional arguments, such as Clear. Is it at all possible to accept any member function of Map as an argument (along with its potential parameters in the form of an ...interface{}), and then call the function, with its correct parameters, on all of the member Maps in a Layered? Something like this, for example:

func (l Layered) MapCall(callMe func(Map, ...interface{}), params ...interface{}) {
    for _, m := range l.data {
        callMe(m, params...)
    }
}

答案1

得分: 1

你可能希望更注重功能性,而不是尝试使用接口。

我将使用你的Interpolate方法提供一个示例。

你可以首先定义一个用于生成操作的辅助函数:

func InterpolateOp(min, max float64) func(Map) {
    return func(m Map) {
        m.Interpolate(min, max)
    }
}

另一个更适合你的MapCall函数的名称是MapInterpolate

然后,在使用MapCall或创建操作列表时,你可以继续使用之前使用的相同类型,即func(Map)

var l Layered
...
l.MapCall(InterpolateOp(2, 4))

这个解决方案的关键是数据绑定到组成函数的闭包中,从而解决了最初的问题:没有足够的数据来调用适当的函数。

在你的情况下,如果有很多方法,这样做可能不值得,那么你可以考虑代码生成,因为它是重复且简单的。这超出了本问题的范围。

另请参阅:https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions

英文:

You may want to be more functional than try to use interfaces.

I will provide an example using your Interpolate method.

You can start by defining a helper function for generating the operation:

func InterpolateOp(min, max float64) func(Map) {
    return func(m Map) {
        m.Interpolate(min, max)
    }
}

Another name that would match your MapCall function better is MapInterpolate.

Then, when using MapCall or creating lists of operations, you can use the same type you were using previously, namely func(Map):

var l Layered
...
l.MapCall(InterpolateOp(2, 4))

The key to this solution is that the data is bound to the closure that makes up the function, which solves the original problem: not enough data to call the appropriate functions.

In your case, if there are so many methods that this is not worth doing, then you may look into code generation, since it is repetitive and easy. That is outside the scope of this question.

See also: https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions

huangapple
  • 本文由 发表于 2021年1月27日 09:41:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/65911739.html
匿名

发表评论

匿名网友

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

确定