如何优雅地检查三个值的相等性?

tim*_*ion 7 convention syntax readability equality go

说我有价值观a,b而且c.我想知道他们是否平等.如果我做

if a == b == c{...}
Run Code Online (Sandbox Code Playgroud)

然后我得到一个编译错误

invalid operation: a == b == c (mismatched types bool and TypeOfABandC)
Run Code Online (Sandbox Code Playgroud)

这很明显,因为这解析为:

(a == b) == c
Run Code Online (Sandbox Code Playgroud)

而且(a == b)是一个布尔.

我当然可以这样做:

if a == b && a == c {...}
Run Code Online (Sandbox Code Playgroud)

然而,这看起来并不是很好看并且让人感到困惑.还有另外一种方法吗?

icz*_*cza 21

事先说明:

如果3个值相等,您最后提出的解决方案最短,最清晰,最有效的比较方法:

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

或者(根据自己的喜好):

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

这个答案的其余部分(后面的内容)只是用语言规范和语言的能力来表达,展示我觉得有趣和有创意的东西.他们并不试图提供卓越的解决方案.


Go Playground上尝试以下所有示例.这些示例建立在Spec:Comparison运算符中定义的比较的术语和结果之上.

一般说明:在下面的示例中,我使用的类型interface{}将适用于您的值具有的任何类型(a,bc),但如果您知道它们是类型的类型int,那么您也可以使用该特定类型(这将提高效率并缩短例子的长度).

随着map一套

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

实际上,我们将所有可比较的值放入mapas键中,如果全部相等,则地图中只有1对,因此地图的"长度"将为1.地图的值类型不会播放任何角色在这里,它可以是任何东西.我使用,int因为这导致最短的复合文字(定义map值).

此解决方案非常灵活,因为您可以使用任意数量的值来测试所有值是否相等,而不仅仅是3.

有阵列

阵列具有可比性(与切片不同):

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

数组比较的结果将是trueif a == bb == c(如果相应的元素相等).

请注意,您也可以对任意数量的值应用此方法.对于5个值,它看起来像这样:

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

有点棘手 map

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

这种复合字面将分配的比较结果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")
}
Run Code Online (Sandbox Code Playgroud)

随着(命名)structs

如果我们struct事先定义一个简单的:

type P struct{ a, b interface{} }
Run Code Online (Sandbox Code Playgroud)

比较将更加紧凑:

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

(注意if语句的表达式必须放在括号中以避免解析歧义 - 否则它是编译时错误!)

用切片

切片不具有可比性,因此我们需要借用以下方面的帮助reflect.DeepEqual():

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

具有辅助功能

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

并使用它:

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

此解决方案也很灵活,您可以AllEquals()使用任意数量的值进行调用.

请注意,AllEquals()如果我们也愿意在reflect.DeepEqual()这里打电话,我们可以使功能更紧凑:

func AllEquals2(v ...interface{}) bool {
    if len(v) < 2 {
        return true
    }
    return reflect.DeepEqual(v[:len(v)-1], v[1:])
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的回答.我会坚持使用== b && b == c.但是,你的棘手的地图很漂亮;) (3认同)