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

int index = val > 0 ? val : -val

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

var index int

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

还有更好的办法吗?


当前回答

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

其他回答

No Go没有三元运算符。使用if/else语法是惯用的方法。

Why does Go not have the ?: operator? There is no ternary testing operation in Go. You may use the following to achieve the same result: if expr { n = trueVal } else { n = falseVal } The reason ?: is absent from Go is that the language's designers had seen the operation used too often to create impenetrably complex expressions. The if-else form, although longer, is unquestionably clearer. A language needs only one conditional control flow construct. — Frequently Asked Questions (FAQ) - The Go Programming Language

现在随着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)

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

我已经编译了一些项目并比较了速度。

/*
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

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

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

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来包装它。


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