2013-08-05 2 views
0

Нормальное использование функциональных переменных в Go позволяет сравнивать их только с nil, а не с другими. Причина этого (как мне объяснили) заключается в том, что, поскольку Go имеет замыкания, определение равенства является нечетким. Если у меня есть два разных закрытия с разными значениями, привязанными к локальным переменным, но которые используют одну и ту же основную функцию, должны ли они считаться равными или неравными?Сравнить значения функций в Go

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

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") 
} 

Есть ли любым способом, например, используя пакеты reflect или unsafe, чтобы заставить это работать?

+0

Существует причина, по которой функции нельзя сравнивать (вы на самом деле назвали ее). Хотя это может быть возможно с отражением пакета, я не вижу никакого использования в приведенном выше примере кода: если вы выполняете 'f: = getFunc (true)' you _know_, то получаете func1. Так почему же сравнивать? – Volker

+0

Это не тот код, который я использую; это просто пример, который поможет вам понять, какое сравнение я пытаюсь сделать (часть 'f == func1'). – joshlf

+0

Возможный дубликат: http: // stackoverflow.com/questions/9643205/how-do-i-compare-two-functions-for-pointer-equal-in-the-top-go-weekly – BurntSushi5

ответ

1

Расширение на ответ @ Luke, кажется, что я могу непосредственно проверить равенство указателя. Обратите внимание, что это действительно так. Цитирую reflect.Value.Pointer() документации:

Если Kind V является Func, возвращаемый указатель является основной код указатель, но не обязательно, достаточно определить одну функцию однозначно. Единственная гарантия заключается в том, что результат равен нулю тогда и только тогда, когда v - значение функции nil.

Тем не менее, вот что вы можете сделать:

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

Обратите внимание, что я запустить батарею тестов (которые я использовал для регрессионного тестирования кода, который в результате @ ответа Луки) против эта новая версия, и все они прошли, что заставило меня поверить, что предупреждение, выпущенное в документации reflect, может быть в порядке, чтобы игнорировать, но тогда игнорирование документации действительно никогда не является хорошей идеей ...

2

Вы могли бы сравнить функции по имени:

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") 
} 

Но это зависит от обоих reflect и runtime пакетов. Наверное, это не очень хорошая идея.

Вам действительно нужно сравнить функции? Я бы рассмотрел альтернативу, если это было возможно.

+0

Да, я определенно делаю. Вариант использования немного странно специфичен. Это для калькулятора rpn, основанного на доказательстве концепции рекурсии: https://github.com/joshlf13/rpn – joshlf

+0

Вот конкретная проблема: https://github.com/joshlf13/rpn/issues/1 – joshlf

+0

@ joshlf13 Интересное , Можно ли было бы изменить оператор оператора типа operator (int) оператора 'type' type operator func (int) (operator, int) '. Возможно, дайте себе что-то дополнительное для сравнения? – Luke

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 } 

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

Где get_uniq_id делает то, что он говорит на коробке. Это становится немного уродливым, потому что Go не имеет перегрузки (), и это более или менее невозможно без дженериков, если вы хотите сделать это для функций в целом. Но это должно работать очень хорошо для ваших целей.

+0

На самом деле это предполагает другое решение, которое заключается в том, чтобы использовать типы вместо функций в целом, чтобы их можно было сравнивать напрямую. В основном вы делаете что-то вроде этого: http://play.golang.org/p/WT3h3pIv1_ (этот пример запутан из-за установки, но на практике он может быть относительно простым). Проблема с этим была бы в том, что это не совсем чистая передача продолжения (это для [rpn] (http://github.com/joshlf13/rpn), btw). – joshlf

Смежные вопросы