Memeriksa kesetaraan dua irisan

274

Bagaimana saya bisa mengecek apakah dua irisan sama?

wei2912
sumber
111
Pertanyaannya sebenarnya adalah tentang tugas yang sederhana, tetapi IMO itu adalah pertanyaan nyata, dengan jawaban yang sangat spesifik. Bagaimana itu bisa ditutup sebagai "bukan pertanyaan nyata" oleh, sejauh yang saya bisa lihat, orang-orang yang saya tidak ingat pernah aktif dalam pertanyaan yang ditandai dengan Go, berada di luar jangkauan saya. Secara khusus: pertanyaannya tidak ambigu, lengkap, sempit untuk satu masalah (walaupun sederhana), tidak bersifat retoris dan dapat dijawab secara tepat dan akurat dalam bentuknya saat ini. The ==Operator didefinisikan dalam Go hanya beberapa jenis, jadi apalagi, pertanyaan ini juga merupakan salah satu yang sah.
zzzz
4
Namun, itu bukan salah satu dari hal-hal yang disebutkan dalam alasan dekat ("tidak dapat dijawab secara wajar dalam bentuk saat ini").
Rich Churcher
9
Hahaha, saya tidak percaya ini ditutup karena "bukan pertanyaan nyata". 1) Tidak sulit untuk mengatakan apa yang ditanyakan. 2) Pertanyaannya tidak ambigu / tidak lengkap / luas / tidak masuk akal. Ini adalah penyalahgunaan!
weberc2
5
Sepertinya saat ini terlalu mudah untuk keliru tombol Downvote ("Saya pikir pertanyaan ini tidak menunjukkan usaha dan tidak ditanyakan dengan baik") dengan tombol Tutup ("Saya pikir itu tidak dapat dijawab karena alasan berikut .. . "). Mungkin karena suara Tutup gratis.
Kos
3
Terjadi untuk mengembangkan di Go dan berlari melawan slice can only be compared to nil, dan bertanya-tanya apakah ada cara golang idiomatik untuk memeriksa kesetaraan irisan ... jika operator kesetaraan tidak ditentukan oleh bahasa, maka saya merasa masuk akal untuk bertanya dengan cara yang paling efisien untuk mencapainya. Pertanyaan tidak perlu ditutup
abgordon

Jawaban:

157

Anda perlu mengulang setiap elemen dalam slice dan tes. Kesetaraan untuk irisan tidak ditentukan. Namun, ada bytes.Equalfungsi jika Anda membandingkan nilai tipe []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
sumber
15
Saran: for i, v := range a { if v != b[i] { return false } }.
zzzz
19
@zzzz Hati-hati, ini akan gagal pada panjang yang berbeda.
FiloSottile
2
Ini tidak berfungsi jika tipe elemen tidak mendukung ==. Juga, IIUC, Go tidak memiliki apa pun seperti obat generik. Ini berarti Anda harus menyalin dan menempelkan fungsi ini untuk setiap jenis elemen yang ingin Anda dukung. Ini jelas sesuatu yang harus dikirim dengan bahasa. Bahkan, memang (meskipun dengan keajaiban refleksi), dan Victor memberikan jawabannya. Fakta bahwa ini dipilih di atas jawaban itu, dan lebih banyak suara adalah hanya menjengkelkan ...
allyourcode
5
Pergi sebagai bahasa cenderung untuk merekomendasikan tidak menggunakan refleksi kecuali benar-benar diperlukan. Ya, itu perlu dilakukan untuk setiap jenis tetapi umumnya bukan sesuatu yang sering Anda lakukan. Juga, reflect.DeepEqual dapat melakukan sesuatu yang tidak Anda harapkan seperti mengatakan dua pointer berbeda sama karena nilai yang mereka tunjukkan sama.
Stephen Weinberg
2
Panjang @FiloSottile diperiksa sebelumnya, loop hanya tercapai jika panjangnya berbeda.
icza
259

Anda harus menggunakan reflect.DeepEqual ()

DeepEqual adalah relaksasi rekursif dari operator Go's ==.

DeepEqual melaporkan apakah x dan y “sama dalam,” didefinisikan sebagai berikut. Dua nilai dari jenis yang identik sangat sama jika salah satu dari kasus berikut ini berlaku. Nilai dari tipe yang berbeda tidak pernah sama dalam.

Nilai-nilai array sangat sama ketika elemen-elemen terkaitnya sangat sama.

Nilai-nilai Struct sangat sama jika bidang yang sesuai, baik yang diekspor maupun yang tidak diekspor, sangat sama.

Nilai fungsi sangat sama jika keduanya nol; kalau tidak mereka tidak sederajat.

Nilai antarmuka sangat sama jika mereka memegang nilai konkrit yang sangat sama.

Nilai peta sangat sama jika mereka adalah objek peta yang sama atau jika mereka memiliki panjang yang sama dan kunci yang sesuai (cocok dengan menggunakan kesetaraan Go) memetakan ke nilai yang sangat sama.

Nilai pointer sangat sama jika mereka sama dengan menggunakan operator Go == atau jika mereka menunjuk ke nilai yang sangat sama.

Nilai slice sangat sama ketika semua hal berikut ini benar: keduanya nihil atau keduanya bukan nihil, keduanya memiliki panjang yang sama, dan keduanya menunjuk ke entri awal yang sama dari array mendasar yang sama (yaitu, & x [0 ] == & y [0]) atau elemen yang sesuai (hingga panjang) sangat sama. Perhatikan bahwa irisan kosong non-nil dan irisan nil (misalnya, [] byte {} dan [] byte (nil)) tidak sama dalam.

Nilai lain - angka, bools, string, dan saluran - sangat sama jika mereka sama dengan menggunakan operator Go ==.

Victor Deryagin
sumber
13
Jawaban yang sangat berguna. Terlepas dari kinerja paket mencerminkan umum, itu sangat bagus untuk memiliki fungsi kesetaraan mendalam pra-dikemas untuk digunakan dalam kasus uji di mana kesederhanaan dan kebenaran adalah yang terpenting.
WeakPointer
15
Saya baru saja menjalankan tolok ukur dan mencerminkan. KeepEqual 150 kali lebih lambat dari satu lingkaran. Hanya FYI jika ada yang ingin menggunakan metode ini dalam produksi.
nikdeapen
2
Itu tidak membandingkan irisan irisan acak dengan item yang sama :(
Hemant_Negi
5
@Hemant_Negi dua irisan tidak sama jika mereka memiliki urutan yang berbeda. Jika Anda ingin membandingkan kesetaraan dua irisan sementara mengabaikan urutan kemudian urutkan dan kemudian periksa, atau pindahkan item dari satu irisan ke peta, dan kemudian periksa bahwa setiap elemen pada irisan lain ada di peta. (tambahan pastikan mereka memiliki panjang yang sama)
robbert229
3
Rob Pike (2011) tentang refleksi di Go, menulis di blog resmi Go: "Ini adalah alat yang ampuh yang harus digunakan dengan hati-hati dan dihindari kecuali sangat diperlukan" blog.golang.org/laws-of-reflection . Saya tidak akan menggunakan refleksi dalam kode produksi hanya untuk membandingkan irisan. Itu fungsi yang mudah untuk ditulis. Tetapi perhatikan bahwa ada kelemahan potensial dalam jawaban yang dipilih untuk pertanyaan ini juga, tergantung pada perilaku apa yang Anda harapkan darinya: ia akan menemukan bahwa irisan yang telah diinisialisasi tetapi masih di len 0 dan cap 0 tidak cocok dengan irisan yang telah dideklarasikan tetapi tidak diinisialisasi.
jrefior
44

Ini hanya contoh menggunakan reflect.DeepEqual () yang diberikan dalam jawaban @ 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))

}

Hasil:

true
false

Cobalah di Go Playground

Akavall
sumber
23

Jika Anda memiliki dua []byte, bandingkan menggunakan byte . Sama . Dokumentasi Golang mengatakan:

Equal mengembalikan boolean yang melaporkan apakah a dan b memiliki panjang yang sama dan berisi byte yang sama. Argumen nil setara dengan irisan kosong.

Pemakaian:

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

Ini akan dicetak

true
false
KeksArmee
sumber
mengapa ini bukan top
lurf jurv
3

Dan untuk saat ini, berikut adalah https://github.com/google/go-cmp yang

dimaksudkan untuk menjadi alternatif yang lebih kuat dan lebih aman reflect.DeepEqualuntuk membandingkan apakah dua nilai secara semantik sama.

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
sumber
1

Jika Anda tertarik untuk menulis tes, maka github.com/stretchr/testify/assert adalah teman Anda.

Impor perpustakaan di awal file:

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

Kemudian di dalam tes yang Anda lakukan:


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

Kesalahan yang diminta adalah:

                Diff:
                --- Expected
                +++ Actual
                @@ -1,4 +1,4 @@
                 ([]int) (len=2) {
                + (int) 1,
                  (int) 2,
                - (int) 2,
                  (int) 1,
Test:           TestEquality_SomeSlice
Gabriel Furstenheim
sumber
assert.EqualPenggunaan internal reflect.DeepEqualyang mungkin membuat tes Anda berjalan lebih lambat dan akhirnya pipa Anda.
Deepak Sah
@DeepakSah Apakah Anda memiliki tolok ukur untuk perbedaan kinerja? Dalam bottleneck kinerja pengalaman saya dalam tes tidak dalam pernyataan yang sama, dan Anda mendapatkan pesan berkualitas hebat yang memiliki peningkatan produktivitas
Gabriel Furstenheim