2012-03-10 13 views
22

In Go, c'è un modo per confrontare due puntatori di funzione non nulli 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?Come si confrontano due funzioni per l'uguaglianza del puntatore nell'ultimo Go settimanale?

A partire da ora, se tento di farlo nel modo straight-forward:

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) 

È la mia comprensione che questo comportamento è stato introdotto di recente.


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

risposta

35

Si noti che esiste una differenza tra uguaglianza e identità. Gli operatori == e != in Go1 stanno confrontando i valori per l'equivalenza (tranne quando si confrontano i canali), non per l'identità. Poiché questi operatori stanno cercando non per mescolare l'uguaglianza e l'identità, Go1 è più coerente di pre-Go1 in questo senso.

L'uguaglianza delle funzioni è diversa dall'identità della funzione.


Una ragione per non permettere == e != su tipi di funzione è la prestazione.Ad esempio, il seguente chiusura non utilizza alcuna variabile dal suo ambiente:

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

Impedire confronti di funzioni permette al compilatore di generare una singola implementazione per la chiusura, invece di richiedere al runtime 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 mediante il pacchetto reflect per determinare l'identità funzione, un codice come

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 
} 

deduce comportamento indefinito. Non ci sono garanzie su ciò che il programma stamperà. Il compilatore può decidere che si unirà a SomeFun e a AnotherFun in un'unica implementazione, nel qual caso la seconda stampa stamperà true. Di fatto, non c'è assolutamente alcuna garanzia che la prima stampa stamperà true (potrebbe, sotto qualche altro compilatore Go1 e tempo di esecuzione, stampare false).


una risposta corretta alla tua domanda iniziale è:

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

Ottima risposta. Grazie! Ottieni sicuramente credito per aver risposto "perché", ma sono confuso riguardo alla tua risposta alla mia domanda iniziale. Sembra che stia testando l'identità delle variabili F1_ID e F2_ID piuttosto che l'identità delle funzioni F1 e F2. Ad esempio, se avessi "var F1_ID2 = F1", allora & F1_ID == e F1_ID2 restituirebbero true se stessimo testando l'identità della funzione; ma restituisce falso. – BurntSushi5

+0

Inoltre, la tua critica del mio approccio all'utilizzo riflette mi preoccupa. Non stai suggerendo che l'identità della funzione di test è impossibile da garantire in Go? – BurntSushi5

+1

Commento1: l'ipotesi nell'ultimo snippet di codice è che una particolare funzione abbia un singolo ID. –

1

weekly.2011-11-18

Mappa e valore funzione di confronto sono ora ha invalidato (tranne che per confronto con zero) come per il piano di Go 1. L'uguaglianza delle funzioni era problematica in alcuni contesti e l'uguaglianza della mappa confronta i puntatori, non il contenuto delle mappe.

Equality

uguaglianza funzione è stato problematico in presenza di chiusure (quando sono due chiusure uguali?)

+3

Non credo che questo risponde davvero alla mia domanda. So che i confronti dei valori di funzione non sono consentiti nel modo in cui sto usando; quello che voglio sapere è se c'è qualche soluzione. Mi piacerebbe anche sapere perché la funzione * pointer * uguaglianza è problematica. – BurntSushi5

+1

@ BurntSushi5 Questa è una buona domanda. Certamente ci sono lingue che hanno chiusure in cui è possibile confrontare i puntatori di funzione. Le chiusure * della stessa funzione * che sono chiuse su variabili diverse non sono uguali in quelle lingue. Sembra che questo sia solo schivare un problema di rappresentazione. Non esiste una ragione fondamentale sottostante per cui questo non è consentito in Go. Potrebbe ** essere ** implementato in modo ragionevole, affidabile e coerente; hanno semplicemente scelto di non farlo. – tchrist

+0

@peterSO In risposta alla tua modifica: non risponde in realtà perché non è possibile utilizzare l'uguaglianza del puntatore della funzione. Se due puntatori di funzione puntano alla stessa posizione di memoria, allora dovrebbero essere uguali. Non sarà tanto utile quanto una forma generale di uguaglianza di funzioni, ma non sarebbe neanche inutile. – BurntSushi5

3

La soluzione dipende dalla 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 struct per associare funzioni con stringhe comparabili, qualcosa di simile,

type nameFunc struct { 
    name string 
    fval func() 
} 

Ho solo avuto un paio di funzioni avevo bisogno di confrontare in modo che fosse più semplice per mantenere una fetta di queste strutture e la scansione la fetta se necessario, confrontando il campo del nome e il fval di spedizione. Se ne hai molti potresti invece usare una mappa. Se le tue funzioni hanno firme diverse potresti usare le interfacce e così via.

+0

Ah bello. Ho anche lavorato su di esso semplicemente cambiando il mio approccio. Grazie per il suggerimento su come mantenere una rappresentazione stringa di una funzione. Non funzionerà bene per quello che sto facendo (consentendo funzioni di callback arbitrarie in una libreria), ma potrebbe essere utile lungo la linea. – BurntSushi5

+0

Il pacchetto reflect ha quello che stavo cercando. Ho aggiornato il mio OP con la risposta. – BurntSushi5

Problemi correlati