如何在最新的Go周刊中比较两个函数的指针相等性?

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

How do I compare two functions for pointer equality in the latest Go weekly?

问题

在Go语言中,有没有办法比较两个非nil的函数指针来测试它们是否相等?我的相等标准是指针相等。如果没有,有没有特定的原因不允许指针相等?

目前,如果我以直接的方式尝试这样做:

package main

import "fmt"

func SomeFun() {
}

func main() {
    fmt.Println(SomeFun == SomeFun)
}

我会得到以下错误:

./func-pointers.go:12: invalid operation: SomeFun == SomeFun (func can only be compared to nil)

据我了解,这种行为是最近引入的。


我找到了使用reflect包的答案;然而Atom在下面建议这实际上会产生未定义的行为。请参阅Atom的帖子以获取更多信息和可能的替代解决方案。

package main

import "fmt"
import "reflect"

func SomeFun() { }

func AnotherFun() { }

func main() {
    sf1 := reflect.ValueOf(SomeFun)
    sf2 := reflect.ValueOf(SomeFun)
    fmt.Println(sf1.Pointer() == sf2.Pointer())

    af1 := reflect.ValueOf(AnotherFun)
    fmt.Println(sf1.Pointer() == af1.Pointer())
}

输出:

true
false
英文:

In Go, is there any way to compare two non-nil function pointers to test for equality? My standard of equality is pointer equality. If not, is there any particular reason why pointer equality is not allowed?

As of now, if I attempt to do this in the straight-forward way:

package main

import "fmt"

func SomeFun() {
}

func main() {
    fmt.Println(SomeFun == SomeFun)
}

I get

./func-pointers.go:12: invalid operation: SomeFun == SomeFun (func can only be compared to nil)

It is my understanding that this behavior was introduced recently.


I've found an answer using the reflect package; however Atom suggests below that this actually produces undefined behavior. See Atom's post for more info and a possible alternative solution.

package main

import "fmt"
import "reflect"

func SomeFun() { }

func AnotherFun() { }

func main() {
    sf1 := reflect.ValueOf(SomeFun)
    sf2 := reflect.ValueOf(SomeFun)
    fmt.Println(sf1.Pointer() == sf2.Pointer())

    af1 := reflect.ValueOf(AnotherFun)
    fmt.Println(sf1.Pointer() == af1.Pointer())
}

Outputs:

true
false

答案1

得分: 62

请注意,相等性和身份之间存在差异。Go1中的运算符==!=用于比较值的等价性(除了比较通道时),而不是身份。因为这些运算符试图混淆相等性和身份,所以在这方面,Go1比之前的版本更一致。

函数的相等性与函数的身份是不同的。


不允许在函数类型上使用==!=的一个原因是性能。例如,下面的闭包没有使用其环境中的任何变量:

f := func(){fmt.Println("foo")}

禁止对函数进行比较使得编译器可以为闭包生成单个实现,而不需要在运行时创建新的闭包。因此,从性能的角度来看,禁止函数比较是一个好的决定。


关于使用reflect包来确定函数身份的问题,像下面的代码:

func SomeFun()    {}
func AnotherFun() {}

func main() {
    sf1 := reflect.ValueOf(SomeFun)
    sf2 := reflect.ValueOf(SomeFun)
    fmt.Println(sf1.Pointer() == sf2.Pointer())  // 输出 true

    af1 := reflect.ValueOf(AnotherFun)
    fmt.Println(sf1.Pointer() == af1.Pointer())  // 输出 false
}

依赖于未定义的行为。对于程序将会输出什么,没有任何保证。编译器可能决定将SomeFunAnotherFun合并为单个实现,这种情况下第二个打印语句将输出true。实际上,第一个打印语句没有任何保证会输出true(在某些其他的Go1编译器和运行时下,它可能输出false)。


对于你最初的问题,一个正确的答案是:

package main

import "fmt"

func F1() {}
func F2() {}

var F1_ID = F1  // 为F1创建一个*唯一*的变量
var F2_ID = F2  // 为F2创建一个*唯一*的变量

func main() {
    f1 := &F1_ID  // 取F1_ID的地址
    f2 := &F2_ID  // 取F2_ID的地址

    // 比较指针
    fmt.Println(f1 == f1)  // 输出 true
    fmt.Println(f1 == f2)  // 输出 false
}
英文:

Note that there is a difference between equality and identity. The operators == and != in Go1 are comparing the values for equivalence (except when comparing channels), not for identity. Because these operators are trying not to mix equality and identity, Go1 is more consistent than pre-Go1 in this respect.

Function equality is different from function identity.


One reason for not allowing == and != on function types is performance. For example, the following closure is not using any variables from its environment:

f := func(){fmt.Println("foo")}

Disallowing comparisons of functions enables the compiler to generate a single implementation for the closure, instead of requiring the run-time to create a new closure (at run-time). So, from performance viewpoint the decision to disallow function comparisons was a good decision.


In relation to using the reflect package to determine function identity, a code like

func SomeFun()    {}
func AnotherFun() {}

func main() {
    sf1 := reflect.ValueOf(SomeFun)
    sf2 := reflect.ValueOf(SomeFun)
    fmt.Println(sf1.Pointer() == sf2.Pointer())  // Prints true

    af1 := reflect.ValueOf(AnotherFun)
    fmt.Println(sf1.Pointer() == af1.Pointer())  // Prints false
}

relies on undefined behavior. There are no guarantees as to what the program will print. The compiler may decide that it will merge SomeFun and AnotherFun into a single implementation, in which case the 2nd print statement would print true. In fact, there is absolutely no guarantee that the 1st print statement will print true (it may, under some other Go1 compiler and run-time, print false).


A correct answer to your original question is:

package main

import "fmt"

func F1() {}
func F2() {}

var F1_ID = F1  // Create a *unique* variable for F1
var F2_ID = F2  // Create a *unique* variable for F2

func main() {
    f1 := &F1_ID  // Take the address of F1_ID
    f2 := &F2_ID  // Take the address of F2_ID

    // Compare pointers
    fmt.Println(f1 == f1)  // Prints true
    fmt.Println(f1 == f2)  // Prints false
}

答案2

得分: 7

解决方法取决于情况。我不得不改变一些比较函数的地方。在某些情况下,我只是做了一些不同的事情,这样我就不需要再比较它们了。在另一种情况下,我使用了一个结构体来将函数与可比较的字符串关联起来,类似于:

type nameFunc struct {
    name string
    fval func()
}

我只需要比较几个函数,所以最简单的方法是保持这些结构体的切片,并根据需要扫描切片,比较名称字段并分派fval。如果你有很多函数,你可以使用map。如果你的函数有不同的签名,你可以使用接口,等等。

英文:

The workaround depends on the situtation. I had to change a couple of places where I was comparing functions. In once case I just did something different so I wouldn't need to compare them any more. In another case I used a struct to associate functions with comparable strings, something like,

type nameFunc struct {
    name string
    fval func()
}

I only had a couple of functions I needed to compare so it was simplest to keep a slice of these structs and scan the slice as needed, comparing the name field and dispatching fval. If you have very many you might use a map instead. If your functions have different signatures you could use interfaces, and so on.

答案3

得分: 3

> weekly.2011-11-18
>
> 现在禁止进行映射和函数值的比较(除了与nil的比较),根据Go 1计划。函数的相等性在某些情况下存在问题,而映射的相等性比较的是指针,而不是映射的内容。
>
>
> 相等性
>
> 在闭包存在的情况下,函数的相等性存在问题(两个闭包何时相等?)

英文:

> weekly.2011-11-18
>
> Map and function value comparisons are now disallowed (except for
> comparison with nil) as per the Go 1 plan. Function equality was
> problematic in some contexts and map equality compares pointers, not
> the maps' content.
>
>
> Equality
>
> Function equality was problematic in the presence of closures (when
> are two closures equal?)

huangapple
  • 本文由 发表于 2012年3月10日 09:42:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/9643205.html
匿名

发表评论

匿名网友

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

确定