34

Go で、2 つの非 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
4

3 に答える 3

55

平等と同一性には違いがあることに注意してください。演算子==!=Go1 では、同一性ではなく、同等性 (チャネルを比較する場合を除く) の値を比較しています。これらの演算子は等価性と同一性を混同しないように努めているため、この点で Go1 は Go1 以前よりも一貫性があります。

関数の等価性は、関数の同一性とは異なります。


==and !=on 関数型を許可しない理由の 1 つは、パフォーマンスです。たとえば、次のクロージャーは、その環境からの変数を使用していません。

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())  // Prints true

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

未定義の動作に依存します。プログラムが何を印刷するかについての保証はありません。コンパイラは、それをマージして単一の実装にすることを決定するSomeFun場合があります。AnotherFunその場合、2 番目の print ステートメントは を出力しますtrue。実際、最初の print ステートメントが印刷されるという保証はまったくありませんtrue(他の Go1 コンパイラとランタイムでは、 print になる可能性がありますfalse)。


元の質問に対する正しい答えは次のとおりです。

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
}
于 2012-03-10T07:40:23.907 に答える
6

回避策は状況によって異なります。関数を比較していた場所をいくつか変更する必要がありました。あるケースでは、何か違うことをしただけなので、これ以上比較する必要はありません。別のケースでは、構造体を使用して、関数を同等の文字列に関連付けました。

type nameFunc struct {
    name string
    fval func()
}

比較する必要がある関数は 2 つしかなかったので、これらの構造体のスライスを保持し、必要に応じてスライスをスキャンして、name フィールドを比較し、fval をディスパッチするのが最も簡単でした。数が非常に多い場合は、代わりにマップを使用できます。関数のシグネチャが異なる場合は、インターフェイスなどを使用できます。

于 2012-03-10T04:23:15.107 に答える
1

毎週.2011-11-18

Go 1 プランに従って、マップと関数の値の比較は許可されなくなりました (nil との比較を除く)。関数の等価性は一部のコンテキストで問題があり、マップの等価性はマップのコンテンツではなくポインターを比較します。

平等

関数の等価性は、クロージャが存在する場合に問題がありました (2 つのクロージャが等しいのはいつですか?)

于 2012-03-10T03:28:32.333 に答える