C的三元运算符的惯用go等价物是什么?

What is the idiomatic Go equivalent of C's ternary operator?

在C/C++(以及该族的许多语言)中,一个常见的习惯用法,根据一个条件来声明和初始化一个变量,使用三元条件运算符:

1
int index = val > 0 ? val : -val

Go没有条件运算符。实现上述相同代码的最惯用方法是什么?我找到了下面的解决方案,但似乎很冗长

1
2
3
4
5
6
7
var index int

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

有更好的吗?


正如所指出的(并且希望毫不意外地),使用if+else确实是在go中处理条件的惯用方法。

除了完整的var+if+else代码块外,这种拼写也经常使用:

1
2
3
4
index := val
if val <= 0 {
    index = -val
}

如果您有一个足够重复的代码块,例如等效的int value = a <= b ? a : b,您可以创建一个函数来保存它:

1
2
3
4
5
6
7
8
9
10
func min(a, b int) int {
    if a <= b {
        return a
    }
    return b
}

...

value := min(a, b)

编译器将内联这样简单的函数,所以它更快、更清晰、更短。


no-go没有三元运算符,使用if/else语法是惯用的方法:http://golang.org/doc/faq go-u有三元形式吗?


假设您有以下三元表达式(在C中):

1
int a = test ? 1 : 2;

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

1
2
3
4
5
6
7
var a int

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

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

我使用了一个立即评估的匿名函数:

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

这样可以确保两个分支也不会被评估。


地图三元很容易阅读,无括号:

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


1
2
3
4
5
6
7
8
9
10
func Ternary(statement bool, a, b interface{}) interface{} {
    if statement {
        return a
    }
    return b
}

func Abs(n int) int {
    return Ternary(n >= 0, n, -n).(int)
}

这不会优于if/else,并且需要强制转换,但会起作用。FY:

基准戒备室-8 100000000 18.8 ns/op

基准ABSIFELSE-8200000000 0.27 ns/op


如果您的所有分支都会产生副作用或计算代价高昂,那么下面的代码将在语义上保留重构:

1
2
3
4
5
6
7
index := func() int {
    if val > 0 {
        return printPositiveAndReturn(val)
    } else {
        return slowlyReturn(-val)  // or slowlyNegate(val)
    }
}();  # exactly one branch will be evaluated

通常没有开销(内联的),而且最重要的是,它不会将命名空间与只使用一次的助手函数(这会妨碍可读性和维护)混淆在一起。活生生的例子

注意,如果你天真地运用古斯塔沃的方法:

1
2
3
4
    index := printPositiveAndReturn(val);
    if val <= 0 {
        index = slowlyReturn(-val);  // or slowlyNegate(val)
    }

你会得到一个有不同行为的程序;以防val <= 0程序会打印一个非正值,而它不应该打印!(类似地,如果反向调用分支,则会通过不必要地调用一个慢函数来引入开销。)


Eold的答案很有趣,很有创意,甚至可能很聪明。

但是,建议改为:

1
2
3
4
5
6
var index int
if val > 0 {
    index = printPositiveAndReturn(val)
} else {
    index = slowlyReturn(-val)  // or slowlyNegate(val)
}

是的,它们都编译成基本上相同的程序集,但是,与调用匿名函数仅仅返回一个值(这个值本来可以写入变量)相比,这个代码更清晰。

基本上,简单清晰的代码比创造性代码更好。

此外,任何使用map文字的代码都不是一个好主意,因为map在go中根本不是轻量级的。自从Go1.3以来,小地图的随机迭代顺序得到了保证,并且为了实现这一点,对于小地图来说,它在内存方面的效率要低得多。

因此,制作和删除许多小地图既耗时又耗时。我有一段代码使用了一个小地图(可能有两个或三个键,但常见的用例只是一个条目),但代码太慢了。我们说的是至少3个数量级的慢于相同的代码重写以使用双片键[index]=>数据[index]映射。可能更多。由于以前运行几分钟的一些操作在几毫秒内就开始完成了。