Comprobar la igualdad de dos rebanadas

¿Cómo puedo verificar si dos rebanadas son iguales, dado que los operadores == y != no son una opción?

package main

import "fmt"

func main() {
    s1 := []int{1, 2}
    s2 := []int{1, 2}
    fmt.Println(s1 == s2)
}

Esto no se compila con:

operación inválida: s1 == s2 (el segmento solo se puede comparar con cero)

preguntado el 09 de marzo de 13 a las 14:03

8 Respuestas

Deberías usar reflejar.DeepEqual()

DeepEqual es una relajación recursiva del operador == de Go.

DeepEqual informa si x e y son "profundamente iguales", definidos de la siguiente manera. Dos valores de tipo idéntico son profundamente iguales si se da uno de los siguientes casos. Los valores de tipos distintos nunca son profundamente iguales.

Los valores de la matriz son profundamente iguales cuando sus elementos correspondientes son profundamente iguales.

Los valores de estructura son profundamente iguales si sus campos correspondientes, tanto exportados como no exportados, son profundamente iguales.

Los valores de func son profundamente iguales si ambos son nulos; de lo contrario, no son profundamente iguales.

Los valores de interfaz son profundamente iguales si tienen valores concretos profundamente iguales.

Los valores del mapa son profundamente iguales si son el mismo objeto de mapa o si tienen la misma longitud y sus claves correspondientes (coincidentes usando la igualdad de Go) se asignan a valores profundamente iguales.

Los valores de los punteros son profundamente iguales si son iguales usando el operador == de Go o si apuntan a valores profundamente iguales.

Los valores de división son profundamente iguales cuando todo lo siguiente es verdadero: ambos son nulos o no nulos, tienen la misma longitud y apuntan a la misma entrada inicial de la misma matriz subyacente (es decir, &x[0 ] == &y[0]) o sus elementos correspondientes (hasta la longitud) son profundamente iguales. Tenga en cuenta que un segmento vacío no nulo y un segmento nulo (por ejemplo, []byte{} y []byte(nil)) no son profundamente iguales.

Otros valores (números, bools, cadenas y canales) son profundamente iguales si son iguales usando el operador == de Go.

Respondido 04 Oct 18, 02:10

Una respuesta muy útil. Independientemente del rendimiento general del paquete de reflexión, es muy bueno tener una función de igualdad profunda preempaquetada para usar en casos de prueba donde la simplicidad y la corrección son primordiales. - Puntero débil

Acabo de ejecutar un punto de referencia y reflexionar. DeepEqual es 150 veces más lento que un bucle. Solo para su información si alguien quiere usar este método en producción. - nikdeapen

No compara rebanadas ordenadas aleatoriamente con los mismos elementos :( - Hemant_Negi

@Hemant_Negi dos rebanadas no son iguales si tienen un orden diferente. Si desea comparar la igualdad de dos sectores ignorando el orden, ordénelos y luego verifique, o mueva los elementos de un sector a un mapa, y luego verifique que cada elemento en el otro sector esté en el mapa. (Además, asegúrese de que tengan la misma longitud) - ladrón229

Rob Pike (en 2011) sobre la reflexión en Go, escribiendo en el blog oficial de Go: "Es una herramienta poderosa que debe usarse con cuidado y evitarse a menos que sea estrictamente necesario". blog.golang.org/leyes-de-reflexión . No usaría la reflexión en el código de producción solo para comparar segmentos. Esa es una función fácil de escribir. Pero tenga en cuenta que también hay una falla potencial en la respuesta elegida a esta pregunta, según el comportamiento que espere de ella: encontrará que los segmentos que se han inicializado pero aún están en len 0 y cap 0 no coinciden con los segmentos que se han inicializado. declarado pero no inicializado. - jrefior

Debe recorrer cada uno de los elementos en el segmento y probar. La igualdad para los cortes no está definida. Sin embargo, hay un bytes.Equal función si está comparando valores de tipo []byte.

func testEq(a, b []Type) bool {
    if len(a) != len(b) {
        return false
    }
    for i := range a {
        if a[i] != b[i] {
            return false
        }
    }
    return true
}

Respondido el 06 de junio de 21 a las 16:06

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

@zzzz Cuidado, esto fallará en diferentes longitudes. - FiloSottile

Esto no funciona si el tipo de elemento no admite ==. Además, IIUC, Go no tiene nada parecido a los genéricos. Esto significa que debe copiar y pegar esta función para cada tipo de elemento que desee admitir. Obviamente, esto es algo que debería enviarse con el lenguaje. De hecho, lo hace (aunque con la magia del reflejo), y Víctor proporciona la respuesta. El hecho de que esto sea elegido por encima de esa respuesta, y más votado es simplemente enloquecedor... - todotucodigo

Go as a language tiende a recomendar no usar la reflexión a menos que sea absolutamente necesario. Sí, tendría que hacerse para cada tipo, pero generalmente no es algo que haga a menudo de todos modos. Además, reflect.DeepEqual puede hacer algo que no espera, como decir que dos punteros diferentes son iguales porque los valores a los que apuntan son iguales. - Esteban Weinberg

@FiloSottile La longitud se verifica de antemano, el bucle solo se alcanza si las longitudes difieren. - icza

Este es solo un ejemplo usando reflejar.DeepEqual() eso se da en la respuesta 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

Pruébalo en Ir al patio de recreo

Respondido el 05 de Septiembre de 19 a las 07:09

En términos de rendimiento, ¿cómo se compara esto con la respuesta aceptada? - Manu Manjunath

Si tienes dos []byte, compararlos usando bytes.igual. La documentación de Golang dice:

Equal devuelve un valor booleano que informa si a y b tienen la misma longitud y contienen los mismos bytes. Un argumento nulo es equivalente a una porción vacía.

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

Esto imprimirá

true
false

Respondido el 05 de enero de 18 a las 21:01

¿Por qué esto no es lo mejor? lurf jurv

@lurfjurv ya que esto solo responde al caso muy específico de [] byte y no a un segmento arbitrario: Petr

Thog no se caare - lurf jurv

Y por ahora, aquí está https://github.com/google/go-cmp que

pretende ser una alternativa más potente y segura a reflect.DeepEqual para comparar si dos valores son semánticamente iguales.

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
}

Respondido 31 Jul 19, 11:07

No se puede utilizar == or != con cortes, pero si puede usarlos con los elementos, entonces Go 1.18 tiene una nueva función para comparar fácilmente dos cortes, slices.Equal:

Equal informa si dos rebanadas son iguales: la misma longitud y todos los elementos iguales. Si las longitudes son diferentes, Igual devuelve falso. De lo contrario, los elementos se comparan en orden de índice creciente y la comparación se detiene en el primer par desigual. Los NaN de coma flotante no se consideran iguales.

El slices la ruta de importación del paquete es golang.org/x/exp/slices. código dentro exp el paquete es experimental, aún no estable. Se moverá a la biblioteca estándar. en Ir 1.19 finalmente.

Sin embargo, puedes usarlo tan pronto como Go 1.18 (Area de juegos)

    sliceA := []int{1, 2}
    sliceB := []int{1, 2}
    equal := slices.Equal(sliceA, sliceB)
    fmt.Println(equal) // true

    type data struct {
        num   float64
        label string
    }

    sliceC := []data{{10.99, "toy"}, {500.49, "phone"}}
    sliceD := []data{{10.99, "toy"}, {200.0, "phone"}}
    equal = slices.Equal(sliceC, sliceD)
    fmt.Println(equal) // true

Si los elementos de la rebanada no permiten == y !=, puedes usar slices.EqualFunc y defina cualquier función de comparación que tenga sentido para el tipo de elemento.

Respondido 14 Jul 22, 10:07

En caso de que esté interesado en escribir una prueba, entonces github.com/stretchr/testify/assert es su amigo.

Importe la biblioteca al principio del archivo:

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

Luego dentro de la prueba haces:


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

El error que aparecerá será:

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

Respondido 18 Abr '19, 19:04

assert.Equal utiliza internamente reflect.DeepEqual lo que podría hacer que sus pruebas se ejecuten más lentamente y eventualmente su canalización. - deepak sah

@DeepakSah ¿Tiene puntos de referencia para la diferencia de rendimiento? En mi experiencia, el cuello de botella de rendimiento en las pruebas no está en la igualdad de afirmación, y obtienes mensajes de gran calidad que tienen un impulso en la productividad: gabriel furstenheim

Podrías usar assert.ElementsMatch(t, a, b) ignorar el orden de los elementos. - Marcelosalloum

Pensé en un buen truco y pensé en compartirlo.

Si lo que te interesa saber es si dos rebanadas son idéntico (es decir, son alias de la misma región de datos) en lugar de simplemente igual (el valor en cada índice de una rebanada es igual al valor en el mismo índice de la otra), entonces puede compararlos de manera eficiente de la siguiente manera:

foo := []int{1,3,5,7,9,11,13,15,17,19}

// these two slices are exactly identical
subslice1 := foo[3:][:4]
subslice2 := foo[:7][3:]

slicesEqual := &subslice1[0]  == &subslice2[0]   && 
               len(subslice1) == len(subslice2)

Hay algunas advertencias para este tipo de comparación, en particular, que no puede comparar segmentos vacíos de esta manera, y que la capacidad de los segmentos no se compara, por lo que esta propiedad de "identicidad" solo es realmente útil cuando se lee de un segmento o resegmentando un subsegmento estrictamente más estrecho, ya que cualquier intento de hacer crecer el segmento se verá afectado por la capacidad de los segmentos. Aún así, es muy útil poder declarar de manera eficiente, "estos dos enormes bloques de memoria son de hecho el mismo bloque, sí o no".

Respondido 15 ago 21, 08:08

tiene paréntesis finales que rompen la sintaxis del código - user4466350

alguien podría querer correr fmt.Printf("%p %p\n", &subslice1[0], &subslice2[0]) para ver que ambos comparten la misma dirección de memoria. y que funciona bien siempre que compare el mismo índice con ambos segmentos. es decir fmt.Printf("%p %p\n", &subslice1[1], &subslice2[1]) etc. - usuario4466350

Lo harán. La resegmentación no reasigna, crea un alias del almacenamiento en uso por el segmento original, y los segmentos también son siempre contiguos, por lo que no hay forma de terminar con un segmento en el que esto sea cierto con algunos índices pero no con otros. - wug

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.