Равенство (идентичность) ломтиков го - PullRequest
0 голосов
/ 26 октября 2018

Мой вопрос немного отличается от этого вопроса , который спрашивает о том, как проверить равенство фрагментов Go.

Как показано в статье , срез Go представляет собой значение , состоящее из трех вещей: указатель на массив, длина сегмента и его емкость (максимальная длина). сегмента). Можно ли (дешево) проверить, равны ли два таких среза, потому что они указывают на один и тот же базовый массив и имеют одинаковые значения длины и емкости (предпочтительно без обхода двух срезов, проверяющих равенство отдельных элементов)? Похоже, что оператор == не определен для срезов.

Вопрос возник, когда я реализовывал битовый вектор (IntSet), который внутренне использует []uint64 для представления элементов, и я наткнулся на реализацию метода func (*IntSet) Equals(that *IntSet) bool, который можно было бы назвать как s.Equals(s).

(Похоже, я мог бы оптимизировать для этого случая, как показано ниже, но вопрос остается:

func (this *IntSet) Equals(that *IntSet) bool {
    if this == that { // use equality of pointers!
        return true
    }
// omitted for brevity 
}

1 Ответ

0 голосов
/ 26 октября 2018

Используя адрес первого элемента

Самый простой способ - просто получить адрес первого элемента срезов и сравнить их (указатели сопоставимы ). Мы можем получить адрес первого элемента, просто используя адресный оператор , например, &s[0]. Если срезы пусты, первого элемента не существует, и в этом случае мы проверяем только, являются ли оба элемента пустыми. Мы также должны сравнить длины ломтиков:

func identical(s1, s2 []int) bool {
    if len(s1) != len(s2) {
        return false
    }

    return len(s1) == 0 || &s1[0] == &s2[0]
}

Я намеренно пропустил сравнение емкости, так как она играет роль только в том случае, если срезы нарезаны.

Эта identical() функция только проверяет идентичность срезов. 2 неидентичных среза могут быть одинаковыми (они могут содержать одинаковые элементы), даже если они не идентичны.

Тестирование:

s := []int{1, 2, 3}
fmt.Println(identical(s, s))

s2 := []int{1, 2, 3}
fmt.Println(identical(s, s2))

Вывод (попробуйте на Go Playground ):

true
false

Использование reflect.SliceHeader

Мы можем выбрать получение и использование дескрипторов слайса, которые содержат указатель, длину и емкость. Это смоделировано reflect.SliceHeader:

type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
}

Чтобы получить reflect.SliceHeader, мы можем использовать пакет unsafe и тип unsafe.Pointer, например:

var s []int = ... // s is a slice

// and h will be its descriptor, of type *reflect.SliceHeader
h := (*reflect.SliceHeader)(unsafe.Pointer(&s))

Простая функция сравнения, которая проверяет, являются ли 2 среза идентичными, то есть они указывают на один и тот же резервный массив и имеют одинаковую длину (независимо от их емкости):

func identical(s1, s2 []int) bool {
    h1 := (*reflect.SliceHeader)(unsafe.Pointer(&s1))
    h2 := (*reflect.SliceHeader)(unsafe.Pointer(&s2))

    return h1.Data == h2.Data && h1.Len == h2.Len
}

Тестирование:

s := []int{1, 2, 3}
fmt.Println(identical(s, s))

s2 := []int{1, 2, 3}
fmt.Println(identical(s, s2))

Вывод (попробуйте на Go Playground ):

true
false
...