在C/ c++(以及该家族的许多语言)中,根据条件声明和初始化变量的常用习语使用三元条件操作符:
int index = val > 0 ? val : -val
Go没有条件运算符。实现上面同一段代码的最惯用的方法是什么?我想出了下面的解决方案,但它似乎相当啰嗦
var index int
if val > 0 {
index = val
} else {
index = -val
}
还有更好的办法吗?
在C/ c++(以及该家族的许多语言)中,根据条件声明和初始化变量的常用习语使用三元条件操作符:
int index = val > 0 ? val : -val
Go没有条件运算符。实现上面同一段代码的最惯用的方法是什么?我想出了下面的解决方案,但它似乎相当啰嗦
var index int
if val > 0 {
index = val
} else {
index = -val
}
还有更好的办法吗?
当前回答
我已经编译了一些项目并比较了速度。
/*
go test ternary_op_test.go -v -bench="^BenchmarkTernaryOperator" -run=none -benchmem
*/
package _test
import (
"testing"
)
func BenchmarkTernaryOperatorIfElse(b *testing.B) {
for i := 0; i < b.N; i++ {
if i%2 == 0 {
_ = i
} else {
_ = -i
}
}
}
// https://stackoverflow.com/a/45886594/9935654
func Ternary(statement bool, a, b interface{}) interface{} {
if statement {
return a
}
return b
}
func BenchmarkTernaryOperatorTernaryFunc(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Ternary(i%2 == 0, i, -i).(int)
}
}
// https://stackoverflow.com/a/34636594/9935654
func BenchmarkTernaryOperatorWithFunc(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = func() int {
if i%2 == 0 {
return i
} else {
return -i
}
}
}
}
// https://stackoverflow.com/a/31483763/9935654
func BenchmarkTernaryOperatorMap(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = map[bool]int{true: i, false: -i}[i%2 == 0]
}
}
输出
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
BenchmarkTernaryOperatorIfElse
BenchmarkTernaryOperatorIfElse-8 1000000000 0.4460 ns/op 0 B/op 0 allocs/op
BenchmarkTernaryOperatorTernaryFunc
BenchmarkTernaryOperatorTernaryFunc-8 1000000000 0.3602 ns/op 0 B/op 0 allocs/op
BenchmarkTernaryOperatorWithFunc
BenchmarkTernaryOperatorWithFunc-8 659517496 1.642 ns/op 0 B/op 0 allocs/op
BenchmarkTernaryOperatorMap
BenchmarkTernaryOperatorMap-8 13429532 82.48 ns/op 0 B/op 0 allocs/op
PASS
ok command-line-arguments 4.365s
其他回答
前言:不争论如果其他是走的路,我们仍然可以玩和找到乐趣的语言支持结构。
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) },
)
在围棋场上试试这些例子。
现在随着go1.18泛型的发布,使用这样的泛型函数非常容易,而且它在整个应用程序中都是可重用的
package main
import (
"fmt"
)
func Ternary[T any](condition bool, If, Else T) T {
if condition {
return If
}
return Else
}
func main() {
fmt.Println(Ternary(1 < 2, "yes", "no")) // yes
fmt.Println(Ternary(1 < 2, 1, 0)) // 1
fmt.Println(Ternary[bool](1 < 2, true, false)) // true
}
注意,如果在这种情况下使用它,它会崩溃。 在这种情况下,只需使用if语句, (因为你传递给函数的是nil指针而不是if语句,如果它是假的就不会调用该section)
var a *string
fmt.Println(Ternary(a != nil, *a, "some thing else"))
解决方案使用函数调用它,因此如果它为false,它将不会被执行
func TernaryPointer[T any](condition bool, If, Else func() T) T {
if condition {
return If()
}
return Else()
}
var pString *string
fmt.Println(TernaryPointer(
pString != nil, // condition
func() string { return *pString }, // true
func() string { return "new data" }, // false
))
但在这种情况下,我认为常规的if语句更干净(除非go在未来添加箭头函数) 操场上
相信这个答案,他已经回答了
正如所指出的(希望这并不令人意外),使用if+else确实是在Go中执行条件语句的惯用方式。
除了完整的var+if+else代码块之外,这种拼写也经常被使用:
index := val
if val <= 0 {
index = -val
}
如果你有一段重复的代码,比如int value = a <= b ?A: b,你可以创建一个函数来保存它:
func min(a, b int) int {
if a <= b {
return a
}
return b
}
...
value := min(a, b)
编译器将内联这些简单的函数,所以它更快,更清楚,更简短。
关于三元运算符的惯用方法,还有一个建议:
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)
}
去操场
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更好,需要施放但有效。仅供参考:
基准基准三元-8 100000000 18.8 ns/op
BenchmarkAbsIfElse-8 2000000000 0.27 ns/op