Verifica dell'uguaglianza di due sezioni


274

Come posso verificare se due sezioni sono uguali?


111
La domanda riguarda davvero un compito semplice, ma IMO è una vera domanda, con una risposta molto specifica. Come possa essere stato chiuso come "non una vera domanda" da, per quanto posso vedere, persone che non ricordo di essere mai stato attivo nelle domande contrassegnate con Go, è al di là di me. In particolare: la domanda non è ambigua, completa, stretta a un singolo (sebbene semplice) problema, non retorica e può essere risolta in modo preciso e accurato nella sua forma attuale. L' ==operatore è definito in Vai solo per alcuni tipi, quindi anche questa domanda è legittima.
zzzz

4
Tuttavia, non è nessuna delle cose menzionate nella ragione stretta ("non si può ragionevolmente rispondere nella sua forma attuale").
Rich Churcher

9
Hahaha, non posso credere che questo sia stato chiuso per "non una vera domanda". 1) Non è difficile dire cosa viene chiesto. 2) La domanda non è ambigua / incompleta / ampia / irragionevole. Questo è piuttosto un abuso!
weberc2,

5
Sembra che al momento sia troppo facile confondere il pulsante Downvote ("Penso che questa domanda non mostri sforzo e non sia ben posto") con il pulsante Chiudi ("Penso che non si possa rispondere a causa del seguente motivo ... . "). Potrebbe essere perché i voti ravvicinati sono gratuiti.
Kos,

3
È successo che si stesse sviluppando in Go e si è imbattuto slice can only be compared to nil, e mi chiedevo se esiste un modo golang idiomatico per controllare l'uguaglianza di fetta ... se l'operatore di uguaglianza non è definito dalla lingua, allora trovo ragionevole chiedere il modo più efficiente per realizzarlo. Non è stato necessario chiudere la
domanda

Risposte:


157

È necessario eseguire il ciclo su ciascuno degli elementi nella sezione e testare. L'uguaglianza per le sezioni non è definita. Tuttavia, esiste una bytes.Equalfunzione se si confrontano valori di tipo []byte.

func testEq(a, b []Type) bool {

    // If one is nil, the other must also be nil.
    if (a == nil) != (b == nil) { 
        return false; 
    }

    if len(a) != len(b) {
        return false
    }

    for i := range a {
        if a[i] != b[i] {
            return false
        }
    }

    return true
}

15
Suggerimento: for i, v := range a { if v != b[i] { return false } }.
zzzz,

19
@zzzz Attenzione, questo fallirà su lunghezze diverse.
FiloSottile,

2
Questo non funziona se il tipo di elemento non supporta ==. Inoltre, IIUC, Go non ha niente di simile ai generici. Ciò significa che è necessario copiare e incollare questa funzione per ciascun tipo di elemento che si desidera supportare. Questo è ovviamente qualcosa che dovrebbe essere spedito con la lingua. In effetti, lo fa (anche se con la magia della riflessione), e Victor fornisce la risposta. Il fatto che questo sia scelto sopra quella risposta, e più votato, è semplicemente esasperante ...
allyourcode,

5
Vai come una lingua tende a raccomandare di non usare la riflessione se non assolutamente necessario. Sì, dovrebbe essere fatto per ogni tipo, ma generalmente non è qualcosa che fai spesso. Inoltre, reflection.DeepEqual può fare qualcosa che non ti aspetti, come dire che due diversi puntatori sono uguali perché i valori che indicano sono uguali.
Stephen Weinberg,

2
@FiloSottile La lunghezza viene verificata in anticipo, il ciclo viene raggiunto solo se le lunghezze differiscono.
Icza,

259

È necessario utilizzare reflection.DeepEqual ()

DeepEqual è un rilassamento ricorsivo dell'operatore == di Go.

DeepEqual riporta se xey sono "profondamente uguali", definito come segue. Due valori di tipo identico sono profondamente uguali se si applica uno dei seguenti casi. I valori di tipi distinti non sono mai profondamente uguali.

I valori di matrice sono profondamente uguali quando i loro elementi corrispondenti sono profondamente uguali.

I valori di Struct sono profondamente uguali se i loro campi corrispondenti, sia esportati che non esportati, sono profondamente uguali.

I valori di Func sono profondamente uguali se entrambi sono nulli; altrimenti non sono profondamente uguali.

I valori dell'interfaccia sono profondamente uguali se contengono valori concreti profondamente uguali.

I valori della mappa sono profondamente uguali se sono lo stesso oggetto mappa o se hanno la stessa lunghezza e le loro chiavi corrispondenti (abbinate usando Go uguaglianza) mappano a valori profondamente uguali.

I valori del puntatore sono profondamente uguali se sono uguali usando l'operatore Go == o se indicano valori profondamente uguali.

I valori di sezione sono profondamente uguali quando sono vere tutte le seguenti condizioni: sono entrambe nulle o entrambe non nulle, hanno la stessa lunghezza e indicano entrambe la stessa voce iniziale dello stesso array sottostante (ovvero & x [0 ] == & y [0]) o i loro elementi corrispondenti (fino alla lunghezza) sono profondamente uguali. Si noti che una sezione vuota non nulla e una sezione zero (ad esempio, [] byte {} e [] byte (zero)) non sono profondamente uguali.

Altri valori - numeri, bool, stringhe e canali - sono profondamente uguali se sono uguali usando l'operatore Go ==.


13
Una risposta molto utile Indipendentemente dal generale riflesso delle prestazioni del pacchetto, è molto bello avere una funzione di uguaglianza profonda preconfezionata per l'uso in casi di test in cui semplicità e correttezza sono fondamentali.
WeakPointer

15
Ho appena eseguito un benchmark e riflesso. DeepEqual è 150 volte più lento di un loop. Cordiali saluti, se qualcuno vuole utilizzare questo metodo in produzione.
nikdeapen,

2
Non confronta le sezioni ordinate casualmente con le stesse voci :(
Hemant_Negi il

5
@Hemant_Negi due sezioni non sono uguali se hanno un ordine diverso. Se si desidera confrontare l'uguaglianza di due sezioni mentre si ignora l'ordine, ordinarle e quindi controllare o spostare gli elementi da una sezione in una mappa, quindi verificare che ciascun elemento sull'altra sezione sia nella mappa. (assicurati inoltre che abbiano la stessa lunghezza)
robbert229

3
Rob Pike (nel 2011) sulla riflessione su Go, scrivendo nel blog ufficiale Go: "È uno strumento potente che dovrebbe essere usato con cura ed evitato se non strettamente necessario" blog.golang.org/laws-of-reflection . Non vorrei usare la riflessione nel codice di produzione solo per confrontare le sezioni. Questa è una funzione facile da scrivere. Ma nota che c'è anche un potenziale difetto nella risposta scelta a questa domanda, a seconda del comportamento che ti aspetti da esso: troverà che le sezioni che sono state inizializzate ma sono ancora a len 0 e il cap 0 non corrispondono alle sezioni che sono state dichiarato ma non inizializzato.
jrefior,

44

Questo è solo un esempio dell'utilizzo di reflection.DeepEqual () riportato nella risposta di @ VictorDeryagin.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := []int {4,5,6}
    b := []int {4,5,6}
    c := []int {4,5,6,7}

    fmt.Println(reflect.DeepEqual(a, b))
    fmt.Println(reflect.DeepEqual(a, c))

}

Risultato:

true
false

Provalo in Go Playground


23

Se ne hai due []byte, confrontali usando bytes.Equal . La documentazione di Golang dice:

Uguale restituisce un valore booleano che indica se aeb hanno la stessa lunghezza e contengono gli stessi byte. Un argomento nullo equivale a una sezione vuota.

Uso:

package main

import (
    "fmt"
    "bytes"
)

func main() {
    a := []byte {1,2,3}
    b := []byte {1,2,3}
    c := []byte {1,2,2}

    fmt.Println(bytes.Equal(a, b))
    fmt.Println(bytes.Equal(a, c))
}

Questo stamperà

true
false

perché questo non è il massimo
lur jurv

3

E per ora, ecco https://github.com/google/go-cmp che

intende essere un'alternativa più potente e più sicura reflect.DeepEqualper confrontare se due valori sono semanticamente uguali.

package main

import (
    "fmt"

    "github.com/google/go-cmp/cmp"
)

func main() {
    a := []byte{1, 2, 3}
    b := []byte{1, 2, 3}

    fmt.Println(cmp.Equal(a, b)) // true
}

1

Nel caso in cui tu sia interessato a scrivere un test, allora github.com/stretchr/testify/assertè tuo amico.

Importa la libreria all'inizio del file:

import (
    "github.com/stretchr/testify/assert"
)

Quindi all'interno del test fai:


func TestEquality_SomeSlice (t * testing.T) {
    a := []int{1, 2}
    b := []int{2, 1}
    assert.Equal(t, a, b)
}

L'errore richiesto sarà:

                Diff:
                --- Expected
                +++ Actual
                @@ -1,4 +1,4 @@
                 ([]int) (len=2) {
                + (int) 1,
                  (int) 2,
                - (int) 2,
                  (int) 1,
Test:           TestEquality_SomeSlice

assert.Equalusi interni reflect.DeepEqualche potrebbero rallentare l'esecuzione dei test e, infine, la pipeline.
Deepak Sah,

@DeepakSah Hai dei benchmark per la differenza di prestazioni? Nella mia esperienza, il collo di bottiglia delle prestazioni nei test non è asserito uguale e ricevi messaggi di grande qualità che aumentano la produttività
Gabriel Furstenheim,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.