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

Скажем, у меня есть значения 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 {...}

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


person timthelion    schedule 17.06.2016    source источник
comment
Вы можете создать статический класс, который принимает переменные аргументы и возвращает логическое значение.   -  person Joseph Evans    schedule 17.06.2016
comment
Что смущает в последнем примере? Это явно то, что происходит, и вы все еще можете опустить транзитивное сравнение b == c.   -  person JimB    schedule 17.06.2016


Ответы (1)


Заранее примечание:

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

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 Playground. Примеры основаны на условиях и результатах сравнений, определенных в Спецификация: Операторы сравнения.

Общее примечание: в приведенных ниже примерах я использовал тип 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 значения не равны.

С анонимными structs

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

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

С (по имени) structs

Если мы заранее определим простой 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:])
}
person icza    schedule 17.06.2016
comment
Спасибо за ответ. Я буду придерживаться a == b && b == c. Тем не менее, ваша хитрая карта довольно точна ;) - person timthelion; 18.06.2016