Как элегантно проверить равенство трех значений?
Скажи у меня есть ценности 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:])
}