Как элегантно проверить равенство трех значений?

Скажи у меня есть ценности a, b а также c, Я хочу выяснить, равны ли они. Если я сделаю

if a == b == c{...}

Тогда я получаю ошибку компиляции

invalid operation: a == b == c (mismatched types bool and TypeOfABandC)

Это довольно очевидно, потому что это анализирует:

(a == b) == c

А также (a == b) это бул.

Конечно я могу сделать:

if a == b && a == c {...}

Тем не менее, это не очень красиво выглядит и кажется запутанным. Есть ли другой способ?

1 ответ

Решение

Записка заранее:

Ваше последнее предлагаемое решение - самый короткий, ясный и эффективный способ сравнения, если 3 значения равны:

if a == b && a == c {
    fmt.Println("Clearest: all 3 are equal")
}

или альтернативно (по вашему вкусу):

if a == b && b == c {
    fmt.Println("Clearest: all 3 are equal")
}

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


Попробуйте все приведенные ниже примеры на игровой площадке Go. Примеры основаны на терминах и результатах сравнений, которые определены в Spec: Операторы сравнения.

Общее примечание: в приведенных ниже примерах я использовал тип interface{} который будет работать независимо от типа ваших значений (a, b а также c), но если вы знаете, что они типа int например, вы также можете использовать этот конкретный тип (что повысит эффективность и сократит продолжительность примеров).

С map как набор

if len(map[interface{}]int{a: 0, b: 0, c: 0}) == 1 {
    fmt.Println("Map set: all 3 are equal")
}

Эффективно мы помещаем все сопоставимые значения в map в качестве ключей, и если все они равны, на карте будет только 1 пара, поэтому "длина" карты будет равна 1. Тип значения карты здесь не играет никакой роли, это может быть что угодно. я использовал int потому что это приводит к самому короткому составному литералу (который определяет map значение).

Это гибкое решение, так как вы можете использовать любое количество значений, чтобы проверить, равны ли все, а не только 3.

С массивами

Массивы сравнимы (в отличие от ломтиков):

if [2]interface{}{a, b} == [2]interface{}{b, c} {
    fmt.Println("Arrays: all 3 are equal")
}

Результат сравнения массивов будет true если a == b а также b == c (если соответствующие элементы равны).

Обратите внимание, что вы можете применять этот метод также к любому числу значений. Это будет выглядеть так для 5 значений:

if [4]interface{}{a, b, c, d} == [4]interface{}{b, c, d, e} {
    fmt.Println("Arrays: all 5 are equal")
}

С хитрым map

if map[interface{}]bool{a: b == c}[b] {
    fmt.Println("Tricky map: all 3 are equal")
}

Этот составной литерал назначит результат сравнения b == c к ключу a, И мы спрашиваем значение, связанное с ключом b, Если a == b результат выражения индексации будет результатом b == c то есть равны ли все 3 значения. Если a != b, то нулевое значение типа значения будет результатом, который false в случае bool Правильно говоря, что все 3 значения не равны.

С анонимным struct s

struct значения также сопоставимы, поэтому:

if struct{ a, b interface{} }{a, b} == struct{ a, b interface{} }{b, c} {
    fmt.Println("Anon structs: all 3 are equal")
}

С (по имени) struct s

Если мы определим простой struct заранее:

type P struct{ a, b interface{} }

Сравнение будет намного компактнее:

if (P{a, b} == P{b, c}) {
    fmt.Println("Structs: all 3 are equal")
}

(Обратите внимание, что выражение if оператор должен быть заключен в скобки, чтобы избежать разбора неоднозначности - иначе это ошибка времени компиляции!)

С ломтиками

Ломтики несопоставимы, поэтому нам понадобится помощь reflect.DeepEqual():

if reflect.DeepEqual([]interface{}{a, b}, []interface{}{b, c}) {
    fmt.Println("Slices: all 3 are equal")
}

С функцией помощника

func AllEquals(v ...interface{}) bool {
    if len(v) > 1 {
        a := v[0]
        for _, s := range v {
            if a != s {
                return false
            }
        }
    }
    return true
}

И используя это:

if AllEquals(a, b, c) {
    fmt.Println("Helper function: all 3 are equal")
}

Это решение также гибкое, вы можете позвонить AllEquals() с любым количеством значений.

Обратите внимание, что мы можем сделать AllEquals() функционировать более компактно, если мы также готовы позвонить reflect.DeepEqual() Вот:

func AllEquals2(v ...interface{}) bool {
    if len(v) < 2 {
        return true
    }
    return reflect.DeepEqual(v[:len(v)-1], v[1:])
}
Другие вопросы по тегам