Подтвердить что ты не робот

Как сравнить две функции для равенства указателей в последней неделе?

В 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)

Насколько я понимаю, такое поведение было введено недавно.


Я нашел ответ, используя пакет отражений; однако Atom предлагает ниже, что фактически создает поведение undefined. Дополнительную информацию см. В статье 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
4b9b3361

Ответ 1

Обратите внимание, что существует различие между равенством и идентичностью. Операторы == и != в Go1 сравнивают значения для эквивалентности (за исключением сравнения каналов), а не для идентичности. Поскольку эти операторы пытаются не смешивать равенство и идентичность, Go1 является более последовательным, чем pre-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())  // Prints true

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

зависит от поведения undefined. Нет никаких гарантий относительно того, что программа будет печатать. Компилятор может решить, что он объединит SomeFun и AnotherFun в одну реализацию, и в этом случае второй оператор печати напечатает true. На самом деле нет абсолютно никакой гарантии, что первый оператор печати напечатает true (он может, в рамках какого-либо другого компилятора Go1 и времени выполнения, напечатать 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
}

Ответ 2

Обходной путь зависит от ситутации. Мне пришлось изменить пару мест, где я сравнивал функции. В одном случае я просто сделал что-то другое, поэтому мне больше не пришлось бы сравнивать их. В другом случае я использовал структуру для сопоставления функций со сравнимыми строками, что-то вроде

type nameFunc struct {
    name string
    fval func()
}

У меня было только несколько функций, которые мне нужно было сравнить, поэтому было проще хранить кусочек этих структур и сканировать срез по мере необходимости, сравнивая поле имени и отправляя fval. Если у вас их очень много, вы можете использовать карту. Если ваши функции имеют разные подписи, вы можете использовать интерфейсы и т.д.

Ответ 3

weekly.2011-11-18

Сравнение сопоставлений карт и функций теперь запрещено (за исключением сравнение с nil) в соответствии с планом Go 1. Равноправие функции проблематичный в некоторых контекстах и ​​равенство карты сравнивает указатели, а не содержимое карт.

Equality

Функциональное равенство было проблематичным при наличии замыканий (когда равны ли два замыкания?)