在Go语言中比较函数值。

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

Compare function values in Go

问题

在Go语言中,普通的函数变量只能与nil进行比较,而不能相互比较。这是因为Go语言具有闭包的特性,所以函数的相等性定义是模糊的。如果我有两个不同的闭包,它们绑定了不同的局部变量值,但使用相同的底层函数,那么它们应该被认为是相等还是不相等呢?

然而,我确实希望能够进行这样的比较。特别是,我有类似下面这样的代码(在我的真实代码中,这个检查实际上是必要的,这只是一个虚拟示例),我将一个函数指针与一个函数字面量进行比较:

func getFunc(which bool) (func ()) {
    if which {
        return func1
    } else {
        return func2
    }
}

func func1() { }
func func2() { }

f := getFunc(true)
if f == func1 {
    fmt.Println("func1")
} else {
    fmt.Println("func2")
}

有没有办法,例如使用reflectunsafe包,使其工作?

英文:

Normal use of function variables in Go allows them to be compared only to nil, not to one another. The reason for this (as it's been explained to me) is that, since Go has closures, the definition of equality is fuzzy. If I have two different closures with different values bound to local variables, but which use the same underlying function, should they be considered equal or unequal?

However, I <i>do</i> want to be able to make such a comparison. In particular, I have code like this (except, in my real code, the check is actually necessary - this is just a dummy example), where I compare a function pointer to a function literal:

func getFunc(which bool) (func ()) {
    if which {
        return func1
    } else {
        return func2
    }
}

func func1() { }
func func2() { }

f := getFunc(true)
if f == func1 {
    fmt.Println(&quot;func1&quot;)
} else {
    fmt.Println(&quot;func2&quot;)
}

Is there any way, for example using the reflect or unsafe packages, to get this to work?

答案1

得分: 4

你可以通过函数名进行比较:

f := getFunc(true)
f1 := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
f2 := runtime.FuncForPC(reflect.ValueOf(func1).Pointer()).Name()
if f1 == f2 {
    fmt.Println("func1")
} else {
    fmt.Println("func2")
}

但是这种方法依赖于reflectruntime包。如果可能的话,可能不是一个好主意。

你真的需要比较函数吗?如果可能的话,我会考虑其他的替代方案。

英文:

You could compare the functions by name:

f := getFunc(true)
f1 := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
f2 := runtime.FuncForPC(reflect.ValueOf(func1).Pointer()).Name()
if f1 == f2 {
	fmt.Println(&quot;func1&quot;)
} else {
	fmt.Println(&quot;func2&quot;)
}

But this relies on both the reflect and runtime packages. Probably not a good idea to do this.

Do you really need to compare functions? I would consider an alternative if possible.

答案2

得分: 2

根据@Luke的回答,似乎我可以直接测试指针的相等性。请注意,这是非常不确定的。引用reflect.Value.Pointer()文档的话:

如果v的类型是Func,返回的指针是底层代码的指针,但不一定足以唯一标识单个函数。唯一的保证是,当且仅当v是nil func Value时,结果为零。

也就是说,你可以这样做:

f := getFunc(true)
f1 := reflect.ValueOf(f).Pointer()
f2 := reflect.ValueOf(func1).Pointer()
eq := f1 == f2

请注意,我对这个新版本进行了一系列测试(我用这些测试来对@Luke的回答产生的代码进行回归测试),它们全部通过了,这让我相信可以忽略reflect文档中发出的警告,但是,忽略文档通常不是一个好主意...

英文:

Extending upon @Luke's answer, it appears that I can directly test pointer equality. Note that this is really iffy. To quote the reflect.Value.Pointer() documentation:

> If v's Kind is Func, the returned pointer is an underlying code
> pointer, but not necessarily enough to identify a single function
> uniquely. The only guarantee is that the result is zero if and only if
> v is a nil func Value.

That said, here's what you can do:

f := getFunc(true)
f1 := reflect.ValueOf(f).Pointer()
f2 := reflect.ValueOf(func1).Pointer()
eq := f1 == f2

Note that I did run a battery of tests (which I had used to regression-test the code that resulted from @Luke's answer) against this new version, and they all passed, which leads me to believe that the warning issued in the reflect documentation may be OK to ignore, but then, ignoring documentation is really never a good idea...

答案3

得分: 1

如果您想比较的所有函数具有相同的签名,您可以这样做:

type cmpFunc struct {
    f func()
    id uint64
}

func (c *cmpFunc) call() { c.f() }
func (c *cmpFunc) equals(other *cmpFunc) { return c.id == other.id }

func makeComparable(f func()) *cmpFunc {
    return &cmpFunc{f, get_uniq_id()}
}

其中get_uniq_id函数根据其名称执行相应的操作。由于Go语言没有()重载,并且如果您想要对一般函数进行此操作,而没有泛型,这可能会变得有些复杂。但是对于您的目的来说,这应该可以很好地工作。

英文:

If all the functions you want to compare have the same signature, you could do something like this:

type cmpFunc struct {
    f func()
    id uint64
}

func (c *cmpFunc) call() { c.f() }
func (c *cmpFunc) equals(other *cmpFunc) { return c.id == other.id }

makeComparable(f func()) *cmpFunc {
    return &amp;cmpFunc{f, get_uniq_id()}
}

Where get_uniq_id does what it says on the box. This gets a bit uglier because Go doesn't have () overloading, and it's more or less impossible without generics if you want to do this for functions in general. But this should work pretty well for your purposes.

huangapple
  • 本文由 发表于 2013年8月6日 04:08:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/18066793.html
匿名

发表评论

匿名网友

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

确定