Перевірка рівності двох скибочок


274

Як я можу перевірити, чи рівні два скибочки?


111
Питання справді стосується простого завдання, але ІМО - це справжнє запитання, з дуже конкретною відповіддю. Як я могла бути закрита як "не справжнє запитання", наскільки я бачу, людей, яких я не можу пригадати, щоб коли-небудь були активними в питаннях Go з тегами, - поза мною. Конкретно: питання неоднозначне, повне, вузьке до єдиної (хоча й простої) проблеми, не риторичне, і на нього можна відповісти точно та точно у своєму нинішньому вигляді. ==Оператор визначено в Go тільки для деяких типів, тому , крім того, це питання також є законним.
zzzz

4
Проте це не будь-яка з речей, згаданих у тісній причині ("не можна обгрунтовано відповісти в її теперішній формі").
Rich Churcher

9
Ха-ха-ха, я не можу повірити, що це закрили для "не справжнього питання". 1) Не важко сказати, про що питають. 2) Питання не є неоднозначним / неповним / широким / необґрунтованим. Це досить зловживання!
weberc2

5
Схоже, наразі занадто просто помилитися кнопкою Downvote ("Я думаю, що це питання не докладає зусиль і недостатньо задається") кнопкою "Закрити" ("Я думаю, що на неї не можна відповісти через наступну причину". . "). Можливо, тому, що закриті голоси безкоштовні.
Кос

3
Сталося, що я розвиваюсь у Go, і наткнувся проти slice can only be compared to nil, і було цікаво, чи є ідіоматичний голанг спосіб перевірити рівність зрізів ... якщо оператор рівності не визначений мовою, то я вважаю розумним запитати найбільш ефективний спосіб здійснити це. Питання закривати не потрібно
abgordon

Відповіді:


157

Потрібно провести петлю над кожним із елементів у зрізі та протестувати. Рівність для скибочок не визначена. Однак є bytes.Equalфункція, якщо ви порівнюєте значення типу []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
Пропозиція: for i, v := range a { if v != b[i] { return false } }.
zzzz

19
@zzzz Обережно, це не вдасться різної тривалості.
FiloSottile

2
Це не працює, якщо тип елемента не підтримує ==. Також IIUC, Go не має нічого подібного до дженериків. Це означає, що ви повинні скопіювати n 'вставити цю функцію для кожного типу елементів, який ви хочете підтримати. Це, очевидно, щось, що має відповідати мові. Насправді це і є (хоча і з магією відображення), і Віктор дає відповідь. Той факт, що це обрано вище, ніж відповідь, і більш
голосуючий,

5
Як мова, як правило, рекомендується не використовувати роздуми, якщо це абсолютно не потрібно. Так, це потрібно робити для кожного типу, але це взагалі не те, що ви часто робите. Крім того, refle.DeepEqual може зробити те, чого ви не очікуєте, наприклад, сказати два різні вказівники рівні, оскільки значення, на які вони вказують, рівні.
Стівен Вайнберг

2
@FiloSottile Length заздалегідь перевіряється, цикл досягається, лише якщо довжини відрізняються.
icza

259

Ви повинні використовувати refle.DeepEqual ()

DeepEqual - рекурсивна релаксація оператора Go ==.

DeepEqual повідомляє, чи x і y "глибоко рівні", визначаються наступним чином. Два значення однакового типу глибоко рівні, якщо застосовується один із наступних випадків. Значення різних типів ніколи не є глибоко рівними.

Значення масиву глибоко рівні, коли їх відповідні елементи глибоко рівні.

Значення структури глибоко рівні, якщо відповідні їм поля, як експортовані, так і неекспортовані, глибоко рівні.

Значення функцій глибоко рівні, якщо обидва є нульовими; інакше вони не є глибоко рівними.

Значення інтерфейсу глибоко рівні, якщо вони мають глибоко рівні конкретні значення.

Значення карт глибоко рівні, якщо вони є одним і тим же об'єктом карти або якщо вони мають однакову довжину, а їхні відповідні ключі (відповідні за допомогою Go рівності) відображають на глибоко рівні значення.

Значення вказівника глибоко рівні, якщо вони рівні за допомогою оператора Go == або якщо вони вказують на глибоко рівні значення.

Значення зрізів глибоко рівні, коли всі наведені нижче дані є істинними: вони обоє нульові, або обидва ненульові, мають однакову довжину, або вони вказують на один і той же початковий запис того ж базового масиву (тобто & x [0 ] == & y [0]) або їх відповідні елементи (до довжини) глибоко рівні. Зауважте, що порожній зріз, який не є нульовим, і нульовий фрагмент (наприклад, [] байт {} і [] байт (нуль)) не є глибоко рівними.

Інші значення - числа, булі, рядки та канали - глибоко рівні, якщо вони рівні, використовуючи оператор Go's ==.


13
Дуже корисна відповідь. Незалежно від загальної продуктивності упаковки, дуже приємно мати попередньо упаковану функцію глибокої рівності для використання в тестових випадках, коли простота та правильність є найважливішими.
WeakPointer

15
Я просто запустив орієнтир і відбивав. DeepEqual в 150 разів повільніше, ніж цикл. Просто FYI, якщо хтось хоче використовувати цей метод у виробництві.
nikdeapen

2
Він не порівнює випадково впорядковані скибочки з одними і тими ж предметами :(
Hemant_Negi

5
@Hemant_Negi два фрагменти не рівні, якщо вони мають інший порядок. Якщо ви хочете порівнювати рівність двох фрагментів, ігноруючи порядок, то сортуйте їх, а потім перевірте, або перенесіть елементи з одного фрагмента на карту, а потім перевірте, чи кожен елемент на іншому фрагменті знаходиться на карті. (додатково переконайтеся, що вони однакової довжини)
robbert229

3
Роб Пайк (у 2011 р.) Про роздуми в програмі Go, написавши в офіційному блозі Go: "Це потужний інструмент, який слід використовувати обережно і уникати, якщо не є строго необхідним" blog.golang.org/laws-of-reflection . Я б не використовував відображення у виробничому коді просто для порівняння фрагментів. Це легка функція для написання. Але зауважте, що в обраній відповіді на це запитання також є потенційний недолік, залежно від того, яку поведінку ви від нього очікуєте: він виявить, що фрагменти, які були ініціалізовані, але все ще знаходяться на len 0 та cap 0, не відповідають фрагментам, які були оголошено, але не ініціалізовано.
jrefior

44

Це лише приклад з використанням refle.DeepEqual (), який наведено у відповіді @ 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))

}

Результат:

true
false

Спробуйте це на майданчику Go Go


23

Якщо у вас є два []byte, порівняйте їх за допомогою байтів.Equal . Документація Golang говорить:

Рівне повертає булеве повідомлення про те, чи є a і b однакової довжини і чи містять однакові байти. Аргумент нуля еквівалентний порожньому фрагменту.

Використання:

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

Це надрукується

true
false

чому це не верх
lurf jurv

3

А наразі ось https://github.com/google/go-cmp котрий

призначений бути більш потужною та безпечнішою альтернативою reflect.DeepEqualпорівнянню того, чи є два значення семантично рівними.

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

Якщо вам цікаво написати тест, то github.com/stretchr/testify/assertваш друг.

Імпортуйте бібліотеку на самому початку файлу:

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

Потім всередині тесту ви зробите:


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

Запропонована помилка буде:

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

assert.Equalвнутрішньо використовує, reflect.DeepEqualщо може зробити ваші тести повільнішими і, зрештою, ваш конвеєр.
Діпак Сах

@DeepakSah У вас є орієнтири різниці в продуктивності? На мій досвід, вузьке місце в тестах не є рівним, і ви отримуєте чудові повідомлення про якість, що збільшує продуктивність
Габріель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.