Go 什么是C';s三元运算符?

Go 什么是C';s三元运算符?,go,ternary-operator,conditional-operator,Go,Ternary Operator,Conditional Operator,在C/C++(以及该系列的许多语言)中,根据条件声明和初始化变量的常用习惯用法使用三元条件运算符: int index = val > 0 ? val : -val Go没有条件运算符。实现上述同一段代码最惯用的方法是什么?我得出了下面的解决方案,但似乎相当冗长 var index int if val > 0 { index = val } else { index = -val } 还有更好的吗?No Go没有三元运算符,使用if/else语法是惯用的方法

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

int index = val > 0 ? val : -val
Go没有条件运算符。实现上述同一段代码最惯用的方法是什么?我得出了下面的解决方案,但似乎相当冗长

var index int

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

还有更好的吗?

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

Go中没有三元测试操作。您可以使用以下方法获得相同的结果:

if expr {
    n = trueVal
} else {
    n = falseVal
}
Go中缺少
?:
的原因是该语言的设计者已经看到过该操作被频繁地用于创建难以理解的复杂表达式。
if-else
表单虽然更长,但无疑更清晰。一种语言只需要一个条件控制流构造

-常见问题解答(FAQ)-Go编程语言

正如所指出的(希望不出意外),使用
if+else
确实是Go中的to-do条件

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

index := val
if val <= 0 {
    index = -val
}
索引:=val

如果val则地图三元图不带括号,易于阅读:

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

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

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

var a int

if test {
  a = 1
} else {
  a = 2
}
但是,这可能不符合您的要求。在我的例子中,代码生成模板需要一个内联表达式

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

a := func() int { if test { return 1 } else { return 2 } }()
这确保了不会同时对两个分支进行计算。

如果您的所有分支都产生副作用或计算成本很高,则以下将是语义保留重构:

index := func() int {
    if val > 0 {
        return printPositiveAndReturn(val)
    } else {
        return slowlyReturn(-val)  // or slowlyNegate(val)
    }
}();  # exactly one branch will be evaluated
通常没有开销(内联),最重要的是,不会让命名空间中只使用一次的助手函数变得混乱(这会妨碍可读性和维护)

请注意,如果您要天真地应用:

索引:=printPositiveAndReturn(val);

如果valeold的答案有趣且富有创造性,甚至可能是聪明的

但是,建议改为:

var index int
if val > 0 {
    index = printPositiveAndReturn(val)
} else {
    index = slowlyReturn(-val)  // or slowlyNegate(val)
}
是的,它们都编译成本质上相同的程序集,但是这段代码比调用匿名函数只是为了返回一个本来可以写入变量的值要清晰得多

基本上,简单明了的代码比创造性的代码要好

此外,任何使用映射文字的代码都不是一个好主意,因为在Go中映射根本不是轻量级的。自从Go 1.3以来,小地图的随机迭代顺序是有保证的,为了实现这一点,小地图的内存效率要低一些

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

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,需要强制转换,但可以工作。供参考:

基准Absternary-8 100000000 18.8纳秒/作品


BenchmarkAbsIfElse-8 200000000000.27 ns/op

前言:不必争辩说,如果else
是一条路,我们仍然可以在支持语言的结构中玩并找到乐趣

下面的
If
construct在我的库中与许多其他方法一起提供,即类型


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
例如,三元doing
max()

三元操作
abs()

这看起来很酷,它简单,优雅,高效(它也是)

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

要实现延迟且仅在需要时进行评估,唯一的选择是使用函数(或方法,或),这些函数仅在需要时调用:

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 }
然后:

例如,当前年份>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()
也有参数(除了返回值之外):

以下是您可以使用它们的方式:

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


独立解决方案也不错,但在某些用途上可能不太清晰。

正如其他人所指出的,golang没有三元运算符或任何等效运算符。这是故意的
i := If(someCondition).Fint(calca, calcb)
i := If(time.Now().Year() > 2020).Fint(calca, calcb)
i := If(time.Now().Year() > 2020).Fint(
    func() int { return 3 },
    func() int { return 4 },
)
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
package lib

func maskIfTrue(mask uint64, predicate bool) uint64 {
  if predicate {
    return mask
  }
  return 0
}
        text    "".maskIfTrue(SB), NOSPLIT|ABIInternal, $0-24
        funcdata        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        funcdata        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        movblzx "".predicate+16(SP), AX
        testb   AL, AL
        jeq     maskIfTrue_pc20
        movq    "".mask+8(SP), AX
        movq    AX, "".~r2+24(SP)
        ret
maskIfTrue_pc20:
        movq    $0, "".~r2+24(SP)
        ret
func zeroOrOne(predicate bool) (result int) {
  if predicate {
    result = 1
  }
  return
}
    movblzx "".predicate+8(SP), AX
    movq    AX, "".result+16(SP)
    ret
package lib

func zeroOrOne(predicate bool) (result int) {
  if predicate {
    result = 1
  }
  return
}

type Vendor1 struct {
    Property1 int
    Property2 float32
    Property3 bool
}

// Vendor2 bit positions.
const (
    Property1Bit = 2
    Property2Bit = 3
    Property3Bit = 5
)

func Convert1To2(v1 Vendor1) (result int) {
    result |= zeroOrOne(v1.Property1 == 1) << Property1Bit
    result |= zeroOrOne(v1.Property2 < 0.0) << Property2Bit
    result |= zeroOrOne(v1.Property3) << Property3Bit
    return
}
    movq    "".v1+8(SP), AX
    cmpq    AX, $1
    seteq   AL
    xorps   X0, X0
    movss   "".v1+16(SP), X1
    ucomiss X1, X0
    sethi   CL
    movblzx AL, AX
    shlq    $2, AX
    movblzx CL, CX
    shlq    $3, CX
    orq     CX, AX
    movblzx "".v1+20(SP), CX
    shlq    $5, CX
    orq     AX, CX
    movq    CX, "".result+24(SP)
    ret