在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
}
有更好的选择吗?
eold的答案有趣且富有创意,甚至很聪明。
然而,建议改为:
var index int
if val > 0 {
index = printPositiveAndReturn(val)
} else {
index = slowlyReturn(-val) // or slowlyNegate(val)
}
是的,它们都可以编译成基本相同的汇编代码,但是这段代码比调用匿名函数返回可以在第一次写入变量中的值更易读。
基本上,简单清晰的代码比创意代码更好。
此外,在Go语言中使用地图字面量的任何代码都不是一个好主意,因为在Go语言中地图根本不是轻量级的。自Go 1.3以来,小地图的随机迭代顺序是保证的,并且为了实现这一点,对于小地图而言,内存使用效率大大降低。
因此,创建和删除许多小地图既消耗空间又耗时。我有一段代码使用了一个小的地图(可能有两个或三个键,但常见用例只有一个条目),但该代码运行得非常缓慢。我们正在谈论至少比重写为使用双切片键[index]=>数据[index]映射的相同代码慢3个数量级。而且很可能更多,因为一些以前需要几分钟才能完成的操作现在在毫秒内完成。
dog slow
后面的最后一部分有点困惑,也许其他人也会感到困惑? - Wolfm := map[string]interface{} { a: 42, b: "stuff" }
,然后在另一个函数中通过迭代进行操作:for key, val := range m { code here }
。切换到双切片系统后:keys = []string{ "a", "b" }, data = []interface{}{ 42, "stuff" }
,然后像这样迭代for i, key := range keys { val := data[i] ; code here }
可以使代码速度提高1000倍。 - Cassy Foesch现在有了泛型,我们可以将这个愚蠢的函数改进一下...
func ternary[RV any](cmp bool, rvt RV) RV {
if cmp {
return rvt
}
var rvf RV
return rvf
}
...然后可以这样使用...
for i, data := range trials {
ps.Pages = append(ps.Pages, PDFPage{
Content: data,
ClassName: ternary(i > 0, "pagebreak"),
})
}
我已经编译了一些项目并比较了速度。
/*
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://dev59.com/zmIj5IYBdhLWcg3ws3Ff#45886594
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://dev59.com/zmIj5IYBdhLWcg3ws3Ff#34636594
func BenchmarkTernaryOperatorWithFunc(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = func() int {
if i%2 == 0 {
return i
} else {
return -i
}
}
}
}
// https://dev59.com/zmIj5IYBdhLWcg3ws3Ff#31483763
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
BenchmarkTernaryOperatorTernaryFunc
被报告为比BenchmarkTernaryOperatorIfElse
更快时,我有点担心。显然,该函数可以完全内联,但这仍然不能解释它可能为什么会更快。您的内核是否运行激进的节能模式(这意味着只有在100%的正常运行时间后才会增加时钟速度)?改变测试顺序是否有任何影响? - Martin Kealey我正在尝试一个不使用三个参数函数的解决方案。 别误会,三个参数的解决方案很好用,但我个人更喜欢明确地命名事物。
我想要的是一个明确的接口,就像这样:
When(<condition>).Then(<true value>).Else(<false value>)
type Else[T any] interface {
ElseDo(fn func() T) T
Else(value T) T
}
type Then[T any] interface {
ThenDo(fn func() T) Else[T]
Then(value T) Else[T]
}
type Condition[T any] struct {
condition bool
thenValue T
thenFn func() T
}
func When[T any](condition bool) Then[T] {
return &Condition[T]{condition: condition}
}
func (c *Condition[T]) ThenDo(fn func() T) Else[T] {
c.thenFn = fn
return c
}
func (c *Condition[T]) Then(value T) Else[T] {
c.thenValue = value
return c
}
func (c *Condition[T]) ElseDo(fn func() T) T {
if c.condition {
return c.then()
}
return fn()
}
func (c *Condition[T]) Else(value T) T {
if c.condition {
return c.then()
}
return value
}
func (c *Condition[T]) then() T {
if c.thenFn != nil {
return c.thenFn()
}
return c.thenValue
}
使用方法:
When[int](something == "expectedValue").Then(0).Else(1)
When[int](value > 0).Then(value).Else(1)
When[int](value > 0).ThenDo(func()int {return value * 4}).Else(1)
When[string](boolean == true).Then("it is true").Else("it is false")
很不幸,我没有找到一种方法可以在调用When
函数时摆脱显式类型。该类型不能通过 Then/Else 的返回类型自动推断出来 ♂️
关于 Go 语言中三元操作符的惯用方式,我有一个建议:
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)
}
三元运算符
。 - Eric