Verificando a igualdade de duas fatias

274

Como posso verificar se duas fatias são iguais?

wei2912
fonte
111
A pergunta realmente é sobre uma tarefa simples, mas na IMO é uma pergunta real, com uma resposta muito específica. Como isso poderia ter sido encerrado como "não uma pergunta real" por, até onde eu posso ver, pessoas das quais não consigo me lembrar de estar ativo nas perguntas marcadas com Go, está além de mim. Especificamente: a pergunta não é ambígua, completa, restrita a um único problema (embora simples), não retórica e pode ser respondida com precisão e exatidão em sua forma atual. O ==operador é definido no Go apenas para alguns tipos; portanto, essa pergunta também é legítima.
Zzzz 10/03
4
Ainda assim, não é uma das coisas mencionadas no motivo mais próximo ("não pode ser razoavelmente respondido em sua forma atual").
Ricos Churcher
9
Hahaha, eu não acredito que isso foi fechado por "não ser uma pergunta real". 1) Não é difícil dizer o que está sendo solicitado. 2) A questão não é ambígua / incompleta / ampla / irracional. Isso é um abuso!
weberc2
5
No momento, parece muito fácil confundir o botão Downvote ("Acho que esta pergunta não mostra esforço e não é bem solicitada") com o botão Fechar ("Acho que não pode ser respondida pelo seguinte motivo .. . "). Pode ser porque votos próximos são gratuitos.
Kos
3
Aconteceu estar se desenvolvendo no Go e se deparou com slice can only be compared to nil, e estava pensando se existe uma maneira idiomática de golang para verificar a igualdade de fatias ... se o operador de igualdade não for definido pelo idioma, acho razoável perguntar da maneira mais eficiente para conseguir isso. Questão não precisa ser fechado
abgordon

Respostas:

157

Você precisa fazer um loop sobre cada um dos elementos na fatia e testar. A igualdade para fatias não está definida. No entanto, existe uma bytes.Equalfunção se você estiver comparando valores do 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
}
Stephen Weinberg
fonte
15
Sugestão: for i, v := range a { if v != b[i] { return false } }.
zzzz
19
@zzzz Cuidado, isso irá falhar em diferentes comprimentos.
FiloSottile 30/03
2
Isso não funciona se o tipo de elemento não suportar ==. Além disso, o IIUC, Go não tem nada parecido com genéricos. Isso significa que você deve copiar e colar esta função para cada tipo de elemento que deseja oferecer suporte. Obviamente, isso é algo que deve ser fornecido com o idioma. De fato, sim (embora com a mágica de refletir), e Victor fornece a resposta. O fato de que este é escolhido acima essa resposta, e mais altamente votado é simplesmente enlouquecedor ...
allyourcode
5
Ir como um idioma tende a recomendar não usar reflexão, a menos que seja absolutamente necessário. Sim, isso precisaria ser feito para cada tipo, mas geralmente não é algo que você faça com frequência. Além disso, reflect.DeepEqual pode fazer algo que você não espera, como dizer que dois ponteiros diferentes são iguais porque os valores que apontam são iguais.
Stephen Weinberg
2
@FiloSottile Length é verificado previamente, o loop só é atingido se os comprimentos diferirem.
icza 4/08/15
259

Você deve usar reflect.DeepEqual ()

DeepEqual é um relaxamento recursivo do operador Go ==.

O DeepEqual relata se xey são "profundamente iguais", definidos da seguinte maneira. Dois valores do tipo idêntico são profundamente iguais se um dos seguintes casos se aplicar. Valores de tipos distintos nunca são profundamente iguais.

Os valores da matriz são profundamente iguais quando seus elementos correspondentes são profundamente iguais.

Os valores de estrutura são profundamente iguais se seus campos correspondentes, exportados e não exportados, forem profundamente iguais.

Os valores de função são profundamente iguais se ambos forem nulos; caso contrário, eles não são profundamente iguais.

Os valores da interface são profundamente iguais se mantiverem valores concretos profundamente iguais.

Os valores do mapa são profundamente iguais se tiverem o mesmo objeto de mapa ou se tiverem o mesmo comprimento e suas chaves correspondentes (correspondidas usando a igualdade Go) são mapeadas para valores profundamente iguais.

Os valores do ponteiro são profundamente iguais se forem iguais usando o operador Go's == ou se apontarem para valores profundamente iguais.

Os valores das fatias são profundamente iguais quando tudo o que se segue é verdadeiro: ambos são nulos ou não são nulos, têm o mesmo comprimento e apontam para a mesma entrada inicial da mesma matriz subjacente (ou seja, x [0 ] == & y [0]) ou seus elementos correspondentes (até o comprimento) são profundamente iguais. Observe que uma fatia vazia não nula e uma fatia nula (por exemplo, [] byte {} e [] byte (nil)) não são profundamente iguais.

Outros valores - números, bools, strings e canais - são profundamente iguais se forem iguais usando o operador Go's ==.

Victor Deryagin
fonte
13
Uma resposta muito útil. Independentemente do desempenho geral do pacote refletir, é muito bom ter uma função de igualdade profunda pré-empacotada para uso em casos de teste em que simplicidade e correção são fundamentais.
WeakPointer
15
Eu apenas executei uma referência e reflito. O DeepEqual é 150 vezes mais lento que um loop. Apenas para sua informação, se alguém quiser usar esse método na produção.
Nikdeapen
2
Não se compara fatias orderded aleatoriamente com os mesmos itens :(
Hemant_Negi
5
@Hemant_Negi duas fatias não são iguais se tiverem uma ordem diferente. Se você quiser comparar a igualdade de duas fatias enquanto ignora a ordem, classifique-as e marque ou mova os itens de uma fatia para um mapa e verifique se cada elemento da outra fatia está no mapa. (Adicionalmente verifique se eles têm mesmo comprimento)
robbert229
3
Rob Pike (em 2011) sobre a reflexão no Go, escrevendo no blog oficial do Go: "É uma ferramenta poderosa que deve ser usada com cuidado e evitada, a menos que seja estritamente necessário" blog.golang.org/laws-of-reflection . Eu não usaria reflexão no código de produção apenas para comparar fatias. Essa é uma função fácil de escrever. Mas observe que também existe uma falha potencial na resposta escolhida para essa pergunta, dependendo do comportamento que você espera dela: ela descobrirá que as fatias que foram inicializadas, mas ainda estão no len 0 e o limite 0 não correspondem às fatias que foram declarado mas não inicializado.
jrefior
44

Este é apenas um exemplo usando reflect.DeepEqual () que é dado na resposta de @ 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))

}

Resultado:

true
false

Experimente no Go Playground

Akavall
fonte
23

Se você tiver dois []byte, compare-os usando bytes.Equal . A documentação de Golang diz:

Igual retorna um valor booleano que informa se aeb têm o mesmo comprimento e contêm os mesmos bytes. Um argumento nulo é equivalente a uma fatia vazia.

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

Isso imprimirá

true
false
KeksArmee
fonte
por que isso não é top
lurf jurv
3

E, por enquanto, aqui está https://github.com/google/go-cmp, que

pretende ser uma alternativa mais poderosa e segura reflect.DeepEqualpara comparar se dois valores são semanticamente iguais.

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
}
lk_vc
fonte
1

Caso você esteja interessado em escrever um teste, então github.com/stretchr/testify/asserté seu amigo.

Importe a biblioteca no início do arquivo:

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

Então, dentro do teste, você faz:


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

O erro solicitado será:

                Diff:
                --- Expected
                +++ Actual
                @@ -1,4 +1,4 @@
                 ([]int) (len=2) {
                + (int) 1,
                  (int) 2,
                - (int) 2,
                  (int) 1,
Test:           TestEquality_SomeSlice
Gabriel Furstenheim
fonte
assert.Equalusa internamente o reflect.DeepEqualque pode fazer com que seus testes sejam mais lentos e, eventualmente, seu pipeline.
Deepak Sah
@DeepakSah Você tem referências para a diferença de desempenho? Na minha gargalo de desempenho experiência nos testes não está no assert iguais, e você receber mensagens de grande qualidade, que tem um impulso na produtividade
Gabriel Furstenheim