Come faccio a confrontare due funzioni per l’uguaglianza dei puntatori nell’ultimo Go settimanale?

In Go, c’è un modo per confrontare due puntatori a funzione non nulla per testare l’uguaglianza? Il mio standard di uguaglianza è l’uguaglianza dei puntatori. In caso contrario, esiste una ragione particolare per cui l’uguaglianza tra puntatori non è consentita?

A partire da ora, se provo a farlo in modo diretto:

package main import "fmt" func SomeFun() { } func main() { fmt.Println(SomeFun == SomeFun) } 

ottengo

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

Sono a conoscenza del fatto che questo comportamento è stato introdotto di recente.


Ho trovato una risposta usando il pacchetto reflect; tuttavia Atom suggerisce di seguito che questo produce un comportamento indefinito. Vedi il post di Atom per maggiori informazioni e una ansible soluzione 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()) } 

Uscite:

 true false 

Nota che c’è una differenza tra uguaglianza e identity framework. Gli operatori == e != In Go1 stanno confrontando i valori per l’equivalenza (tranne quando si confrontano i canali), non per l’id quadro. Poiché questi operatori cercano di non mescolare l’uguaglianza e l’id quadro, Go1 è più coerente di pre-Go1 in questo senso.

L’uguaglianza delle funzioni è diversa dall’id quadro della funzione.


Una ragione per non consentire i tipi == e != On è la prestazione. Ad esempio, la chiusura seguente non utilizza alcuna variabile dal suo ambiente:

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

Disabilitare il confronto delle funzioni consente al compilatore di generare una singola implementazione per la chiusura, invece di richiedere il tempo di esecuzione per creare una nuova chiusura (in fase di esecuzione). Quindi, dal punto di vista delle prestazioni, la decisione di non consentire confronti tra le funzioni è stata una buona decisione.


In relazione all’uso del pacchetto reflect per determinare l’id quadro della funzione, un codice simile

 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 } 

si basa su un comportamento indefinito . Non ci sono garanzie su ciò che il programma stamperà. Il compilatore può decidere che unirà SomeFun e AnotherFun in un’unica implementazione, nel qual caso la seconda stampa verrà SomeFun AnotherFun . In effetti, non c’è assolutamente alcuna garanzia che la prima dichiarazione di stampa venga stampata correttamente (potrebbe, sotto qualche altro compilatore e run-time di Go1, stampare false ).


Una risposta corretta alla tua domanda originale è:

 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 } 

La soluzione alternativa dipende dal situtation. Ho dovuto cambiare un paio di punti in cui stavo confrontando le funzioni. In un caso ho appena fatto qualcosa di diverso, quindi non avrei più bisogno di confrontarli. In un altro caso ho usato una struttura per associare funzioni a stringhe simili, qualcosa del genere,

 type nameFunc struct { name string fval func() } 

Avevo solo un paio di funzioni che dovevo confrontare, quindi era più semplice mantenere una porzione di queste strutture e scansionare la fetta secondo le necessità, confrontando il campo del nome e la distribuzione di fval. Se ne hai molti potresti invece usare una mappa. Se le tue funzioni hanno firme diverse potresti usare le interfacce e così via.

weekly.2011-11-18

I confronti tra i valori delle mappe e delle funzioni non sono ora consentiti (eccetto per il confronto con zero) come previsto dal piano Go 1. L’uguaglianza delle funzioni era problematica in alcuni contesti e l’uguaglianza della mappa confronta i puntatori, non il contenuto delle mappe.

Uguaglianza

L’uguaglianza delle funzioni era problematica in presenza di chiusure (a parità di due chiusure?)