2012-03-10 9 views
22

En Go, ¿hay alguna forma de comparar dos punteros de función que no sean nulos para probar la igualdad? Mi estándar de igualdad es la igualdad del puntero. Si no, ¿hay alguna razón particular por la cual la igualdad del puntero no está permitida?¿Cómo comparo dos funciones para la igualdad del puntero en la última versión de Go weekly?

A partir de ahora, si intento hacer esto de la manera recta hacia adelante:

package main 

import "fmt" 

func SomeFun() { 
} 

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

me sale

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

Es mi entendimiento de que este comportamiento se introdujo recientemente.


He encontrado una respuesta utilizando el paquete de reflejar; sin embargo, Atom sugiere a continuación que esto realmente produce un comportamiento indefinido. Consulte la publicación de Atom para obtener más información y una posible solución alternativa.

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

Salidas:

true 
false 

Respuesta

35

Tenga en cuenta que existe una diferencia entre igualdad e identidad. Los operadores == y != en Go1 están comparando los valores de equivalencia (excepto cuando se comparan canales), no para la identidad. Debido a que estos operadores están intentando no para mezclar igualdad e identidad, Go1 es más consistente que pre-Go1 a este respecto.

La igualdad de funciones es diferente de la identidad de la función.


Una razón para no permitir == y != sobre los tipos de función es el rendimiento.Por ejemplo, el siguiente cierre no está usando ninguna de las variables de su entorno:

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

No permitir comparaciones de funciones permite al compilador para generar una única aplicación para el cierre, en lugar de requerir que el tiempo de ejecución para crear un nuevo cierre (en tiempo de ejecución). Entonces, desde el punto de vista del desempeño, la decisión de no permitir las comparaciones de funciones fue una buena decisión.


En relación con el uso del paquete reflect para determinar la identidad de función, un código como

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 
} 

se basa en un comportamiento indefinido . No hay garantías en cuanto a lo que imprimirá el programa. El compilador puede decidir que combinará SomeFun y AnotherFun en una sola implementación, en cuyo caso la segunda instrucción de impresión imprimirá true. De hecho, no hay absolutamente ninguna garantía de que la primera declaración de impresión imprima true (puede, bajo otro compilador Go1 y tiempo de ejecución, imprimir false).


Una respuesta correcta a la pregunta original es:

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 
} 
+1

Excelente respuesta. ¡Gracias! Definitivamente tienes crédito por responder "por qué", pero estoy confundido acerca de tu respuesta a mi pregunta original. Parece que está probando la identidad de las variables F1_ID y F2_ID en lugar de la identidad de las funciones F1 y F2. Por ejemplo, si tuviera 'var F1_ID2 = F1', entonces & F1_ID == & F1_ID2 devolvería verdadero si estuviéramos probando la identidad de la función; pero devuelve falso. – BurntSushi5

+0

Además, su crítica a mi enfoque de uso de reflect me preocupa. ¿No está sugiriendo que probar la identidad de la función es imposible de garantizar en Go? – BurntSushi5

+1

Comentario1: La suposición en el último fragmento de código es que una función en particular tiene una única ID. –

1

weekly.2011-11-18

Mapa y valor de la función comparaciones están ahora no permitidos (excepto para comparación con nula) según el plan Go 1. La igualdad de funciones fue problemática en algunos contextos y la igualdad de mapas compara punteros, no el contenido de los mapas.

Equality

igualdad función era problemático en la presencia de cierres (cuando son dos cierres iguales?)

+3

No creo que esto realmente responde a mi pregunta. Sé que las comparaciones de valores de función no están permitidas en la forma en que estoy usando; lo que quiero saber es si hay alguna solución. También me gustaría saber por qué la función * puntero * igualdad es problemática. – BurntSushi5

+1

@ BurntSushi5 Esta es una buena pregunta. Ciertamente hay idiomas que tienen cierres donde se pueden comparar indicadores de función. Los cierres * de la misma función * que se cierran sobre diferentes variables no son iguales en esos idiomas. Parece que esto es solo esquivar un problema de representación. No hay ninguna razón fundamental subyacente por la cual esto no se permite en Go. ** Podría ** implementarse de manera sensible, confiable y consistente; simplemente han elegido no hacerlo. – tchrist

+0

@peterSO En respuesta a su edición: Eso realmente no responde por qué no se puede usar la igualdad del puntero a la función. Si dos punteros a función apuntan a la misma ubicación de memoria, entonces deberían ser iguales. No será tan útil como una forma general de igualdad de funciones, pero tampoco sería inútil. – BurntSushi5

3

La solución depende de la situtation. Tuve que cambiar un par de lugares donde estaba comparando funciones. En una ocasión, hice algo diferente para no tener que compararlos más. En otro caso he usado una estructura para asociar funciones con cadenas comparables, algo así como,

type nameFunc struct { 
    name string 
    fval func() 
} 

sólo había un par de funciones que sea precisa para comparar lo que fue más sencilla de mantener una rebanada de estas estructuras y escanear la rebanada según sea necesario, comparando el campo de nombre y enviando fval. Si tiene muchos, puede usar un mapa en su lugar. Si sus funciones tienen diferentes firmas, puede usar interfaces, y así sucesivamente.

+0

Ah, bien. También he trabajado alrededor cambiando mi enfoque. Gracias por la sugerencia de mantener una cadena de representación de una función. No funcionará bien para lo que estoy haciendo (permitiendo funciones de devolución de llamada arbitrarias en una biblioteca), pero puede ser útil en el futuro. – BurntSushi5

+0

El paquete de reflejar tiene lo que estaba buscando. He actualizado mi OP con la respuesta. – BurntSushi5

Cuestiones relacionadas