在C/ c++(以及该家族的许多语言)中,根据条件声明和初始化变量的常用习语使用三元条件操作符:

int index = val > 0 ? val : -val

Go没有条件运算符。实现上面同一段代码的最惯用的方法是什么?我想出了下面的解决方案,但它似乎相当啰嗦

var index int

if val > 0 {
    index = val
} else {
    index = -val
}

还有更好的办法吗?


当前回答

前言:不争论如果其他是走的路,我们仍然可以玩和找到乐趣的语言支持结构。

Go 1.18泛型更新:Go 1.18增加了泛型支持。现在可以创建一个像这样的泛型If()函数。注意:可以在github.com/icza/gog上找到gog.If()(披露:我是作者)。

func If[T any](cond bool, vtrue, vfalse T) T {
    if cond {
        return vtrue
    }
    return vfalse
}

你可以这样用:

min := If(i > 0, i, 0)

1.18之前的答案如下:


下面的If构造在我的github.com/icza/gox库中有很多其他方法,就是gox。如果类型。


Go允许将方法附加到任何用户定义的类型,包括bool这样的基本类型。我们可以创建一个自定义类型,将bool作为其底层类型,然后通过对条件进行简单的类型转换,就可以访问它的方法。从操作数中接收和选择的方法。

就像这样:

type If bool

func (c If) Int(a, b int) int {
    if c {
        return a
    }
    return b
}

我们如何使用它?

i := If(condition).Int(val1, val2)  // Short variable declaration, i is of type int
     |-----------|  \
   type conversion   \---method call

例如,执行max()的三元函数:

i := If(a > b).Int(a, b)

一个三元做abs():

i := If(a >= 0).Int(a, -a)

这看起来很酷,它简单,优雅,高效(它也适合内联)。

与“真正的”三元运算符相比,它的一个缺点是:它总是计算所有操作数。

要实现延迟的和仅当需要时的求值,唯一的选择是使用函数(声明的函数或方法,或函数字面量),这些函数只在需要时调用:

func (c If) Fint(fa, fb func() int) int {
    if c {
        return fa()
    }
    return fb()
}

让我们假设我们有这些函数来计算a和b:

func calca() int { return 3 }
func calcb() int { return 4 }

然后:

i := If(someCondition).Fint(calca, calcb)

例如,条件是当前年份> 2020:

i := If(time.Now().Year() > 2020).Fint(calca, calcb)

如果我们想使用函数字面量:

i := If(time.Now().Year() > 2020).Fint(
    func() int { return 3 },
    func() int { return 4 },
)

最后注意:如果你有不同签名的函数,你不能在这里使用它们。在这种情况下,你可以使用带有匹配签名的函数文字来使它们仍然适用。

例如,如果calca()和calcb()也有参数(除了返回值):

func calca2(x int) int { return 3 }
func calcb2(x int) int { return 4 }

你可以这样使用它们:

i := If(time.Now().Year() > 2020).Fint(
    func() int { return calca2(0) },
    func() int { return calcb2(0) },
)

在围棋场上试试这些例子。

其他回答

假设你有以下三元表达式(C语言):

int a = test ? 1 : 2;

在Go中惯用的方法是简单地使用if块:

var a int

if test {
  a = 1
} else {
  a = 2
}

但是,这可能不符合您的要求。在我的例子中,我需要一个代码生成模板的内联表达式。

我使用了一个立即求值的匿名函数:

a := func() int { if test { return 1 } else { return 2 } }()

这确保了两个分支都不会被计算。

没有括号,map三元很容易读:

c := map[bool]int{true: 1, false: 0} [5 > 4]

关于三元运算符的惯用方法,还有一个建议:

package main

import (
    "fmt"
)

func main() {
    val := -5

    index := func (test bool, n, d int) int {
        if test {
            return n
        }
        return d
    }(val > 0, val, -val)
    
    fmt.Println(index)
}

去操场

前言:不争论如果其他是走的路,我们仍然可以玩和找到乐趣的语言支持结构。

Go 1.18泛型更新:Go 1.18增加了泛型支持。现在可以创建一个像这样的泛型If()函数。注意:可以在github.com/icza/gog上找到gog.If()(披露:我是作者)。

func If[T any](cond bool, vtrue, vfalse T) T {
    if cond {
        return vtrue
    }
    return vfalse
}

你可以这样用:

min := If(i > 0, i, 0)

1.18之前的答案如下:


下面的If构造在我的github.com/icza/gox库中有很多其他方法,就是gox。如果类型。


Go允许将方法附加到任何用户定义的类型,包括bool这样的基本类型。我们可以创建一个自定义类型,将bool作为其底层类型,然后通过对条件进行简单的类型转换,就可以访问它的方法。从操作数中接收和选择的方法。

就像这样:

type If bool

func (c If) Int(a, b int) int {
    if c {
        return a
    }
    return b
}

我们如何使用它?

i := If(condition).Int(val1, val2)  // Short variable declaration, i is of type int
     |-----------|  \
   type conversion   \---method call

例如,执行max()的三元函数:

i := If(a > b).Int(a, b)

一个三元做abs():

i := If(a >= 0).Int(a, -a)

这看起来很酷,它简单,优雅,高效(它也适合内联)。

与“真正的”三元运算符相比,它的一个缺点是:它总是计算所有操作数。

要实现延迟的和仅当需要时的求值,唯一的选择是使用函数(声明的函数或方法,或函数字面量),这些函数只在需要时调用:

func (c If) Fint(fa, fb func() int) int {
    if c {
        return fa()
    }
    return fb()
}

让我们假设我们有这些函数来计算a和b:

func calca() int { return 3 }
func calcb() int { return 4 }

然后:

i := If(someCondition).Fint(calca, calcb)

例如,条件是当前年份> 2020:

i := If(time.Now().Year() > 2020).Fint(calca, calcb)

如果我们想使用函数字面量:

i := If(time.Now().Year() > 2020).Fint(
    func() int { return 3 },
    func() int { return 4 },
)

最后注意:如果你有不同签名的函数,你不能在这里使用它们。在这种情况下,你可以使用带有匹配签名的函数文字来使它们仍然适用。

例如,如果calca()和calcb()也有参数(除了返回值):

func calca2(x int) int { return 3 }
func calcb2(x int) int { return 4 }

你可以这样使用它们:

i := If(time.Now().Year() > 2020).Fint(
    func() int { return calca2(0) },
    func() int { return calcb2(0) },
)

在围棋场上试试这些例子。

虽然被创作者所回避,但俏皮话也有自己的一席之地。

这个函数解决了惰性求值的问题,它允许你在必要时可选地传递函数来求值:

func FullTernary(e bool, a, b interface{}) interface{} {
    if e {
        if reflect.TypeOf(a).Kind() == reflect.Func {
            return a.(func() interface{})()
        }
        return a
    }
    if reflect.TypeOf(b).Kind() == reflect.Func {
        return b.(func() interface{})()
    }
    return b
}

func demo() {
    a := "hello"
    b := func() interface{} { return a + " world" }
    c := func() interface{} { return func() string { return "bye" } }
    fmt.Println(FullTernary(true, a, b).(string)) // cast shown, but not required
    fmt.Println(FullTernary(false, a, b))
    fmt.Println(FullTernary(true, b, a))
    fmt.Println(FullTernary(false, b, a))
    fmt.Println(FullTernary(true, c, nil).(func() string)())
}

输出

hello
hello world
hello world
hello
bye

传入的函数必须返回一个接口{}以满足内部强制转换操作。 根据上下文,您可以选择将输出强制转换为特定类型。 如果你想从这个函数中返回一个函数,你需要用c来包装它。


这里的独立解决方案也很好,但对于某些用途可能不太清楚。