Fab*_*ien 248 ternary-operator go conditional-operator
在C/C++(以及该系列的许多语言)中,根据条件声明和初始化变量的常用习惯用法是使用三元条件运算符:
int index = val > 0 ? val : -val
Run Code Online (Sandbox Code Playgroud)
Go没有条件运算符.实现上述相同代码的最惯用方法是什么?我来到以下解决方案,但它似乎相当冗长
var index int
if val > 0 {
index = val
} else {
index = -val
}
Run Code Online (Sandbox Code Playgroud)
还有更好的东西吗?
Gus*_*yer 211
正如所指出的那样(并且毫无疑问),使用if+else确实是在Go中进行条件限制的惯用方法.
但是,除了完整var+if+else的代码块之外,这种拼写还经常被使用:
index := val
if val <= 0 {
index = -val
}
Run Code Online (Sandbox Code Playgroud)
如果你有足够重复的代码块,比如相当于int value = a <= b ? a : b,你可以创建一个函数来保存它:
func min(a, b int) int {
if a <= b {
return a
}
return b
}
...
value := min(a, b)
Run Code Online (Sandbox Code Playgroud)
编译器将内联这些简单的函数,因此它更快,更清晰,更短.
ish*_*aaq 63
No Go没有三元运算符,使用if/else语法是惯用的方式:http://golang.org/doc/faq#Does_Go_have_a_ternary_form
Pet*_*yer 51
假设您有以下三元表达式(在C中):
int a = test ? 1 : 2;
Run Code Online (Sandbox Code Playgroud)
Go中惯用的方法是简单地使用一个if块:
var a int
if test {
a = 1
} else {
a = 2
}
Run Code Online (Sandbox Code Playgroud)
但是,这可能不符合您的要求.就我而言,我需要一个代码生成模板的内联表达式.
我使用了一个立即评估的匿名函数:
a := func() int { if test { return 1 } else { return 2 } }()
Run Code Online (Sandbox Code Playgroud)
这可确保不对两个分支进行评估.
小智 34
地图三元很容易阅读,没有括号:
c := map[bool]int{true: 1, false: 0} [5 > 4]
Run Code Online (Sandbox Code Playgroud)
icz*_*cza 13
前言:无需争论这if else是要走的路,我们仍然可以在支持语言的结构中玩耍并从中找到乐趣。
If我的github.com/icza/gox库中提供了以下构造以及许多其他方法,即gox.If类型。
Go 允许将方法附加到任何用户定义的类型,包括诸如bool. 我们可以创建一个自定义类型bool作为其基础类型,然后在条件下进行简单的类型转换,我们可以访问它的方法。从操作数接收和选择的方法。
像这样的东西:
type If bool
func (c If) Int(a, b int) int {
if c {
return a
}
return b
}
Run Code Online (Sandbox Code Playgroud)
我们如何使用它?
i := If(condition).Int(val1, val2) // Short variable declaration, i is of type int
|-----------| \
type conversion \---method call
Run Code Online (Sandbox Code Playgroud)
例如一个三元做max():
i := If(a > b).Int(a, b)
Run Code Online (Sandbox Code Playgroud)
三元做abs():
i := If(a >= 0).Int(a, -a)
Run Code Online (Sandbox Code Playgroud)
这看起来很酷,它简单、优雅且高效(它也适用于内联)。
与“真正的”三元运算符相比的一个缺点:它总是评估所有操作数。
为了实现延迟和仅当需要的评估,唯一的选择是使用函数(声明的函数或方法,或函数文字),它们仅在/如果需要时调用:
func (c If) Fint(fa, fb func() int) int {
if c {
return fa()
}
return fb()
}
Run Code Online (Sandbox Code Playgroud)
使用它:让我们假设我们有这些函数来计算a和b:
func calca() int { return 3 }
func calcb() int { return 4 }
Run Code Online (Sandbox Code Playgroud)
然后:
i := If(someCondition).Fint(calca, calcb)
Run Code Online (Sandbox Code Playgroud)
例如,条件是当前年份 > 2020:
i := If(time.Now().Year() > 2020).Fint(calca, calcb)
Run Code Online (Sandbox Code Playgroud)
如果我们想使用函数字面量:
i := If(time.Now().Year() > 2020).Fint(
func() int { return 3 },
func() int { return 4 },
)
Run Code Online (Sandbox Code Playgroud)
最后一点:如果你有不同签名的函数,你不能在这里使用它们。在这种情况下,您可以使用具有匹配签名的函数文字来使它们仍然适用。
例如 ifcalca()和calcb()也会有参数(除了返回值):
func calca2(x int) int { return 3 }
func calcb2(x int) int { return 4 }
Run Code Online (Sandbox Code Playgroud)
这是您可以使用它们的方式:
i := If(time.Now().Year() > 2020).Fint(
func() int { return calca2(0) },
func() int { return calcb2(0) },
)
Run Code Online (Sandbox Code Playgroud)
在Go Playground上试试这些例子。
Phi*_*iny 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)
}
Run Code Online (Sandbox Code Playgroud)
这不会超过if/else并且需要强制转换但有效.供参考:
BenchmarkAbsTernary-8 100000000 18.8 ns/op
BenchmarkAbsIfElse-8 2000000000 0.27 ns/op
正如其他人所指出的,golang 没有三元运算符或任何等效运算符。这是为了提高可读性而经过深思熟虑的决定。
\n这最近让我遇到了一个场景,以非常有效的方式构建位掩码在惯用方式编写时变得难以阅读,或者在封装为函数时变得非常低效,或者两者兼而有之,因为代码会产生分支:
\npackage lib\n\nfunc maskIfTrue(mask uint64, predicate bool) uint64 {\n if predicate {\n return mask\n }\n return 0\n}\nRun Code Online (Sandbox Code Playgroud)\n生产:
\n text "".maskIfTrue(SB), NOSPLIT|ABIInternal, $0-24\n funcdata $0, gclocals\xc2\xb733cdeccccebe80329f1fdbee7f5874cb(SB)\n funcdata $1, gclocals\xc2\xb733cdeccccebe80329f1fdbee7f5874cb(SB)\n movblzx "".predicate+16(SP), AX\n testb AL, AL\n jeq maskIfTrue_pc20\n movq "".mask+8(SP), AX\n movq AX, "".~r2+24(SP)\n ret\nmaskIfTrue_pc20:\n movq $0, "".~r2+24(SP)\n ret\nRun Code Online (Sandbox Code Playgroud)\n我从中学到的是要更多地利用 Go;在函数中使用命名结果可以节省我在(result int)函数中声明它的一行(并且您可以对捕获执行相同的操作),但是编译器也可以识别这个习惯用法(仅分配一个值 IF)并替换它(如果可能)条件指令。
func zeroOrOne(predicate bool) (result int) {\n if predicate {\n result = 1\n }\n return\n}\nRun Code Online (Sandbox Code Playgroud)\n产生无分支结果:
\n movblzx "".predicate+8(SP), AX\n movq AX, "".result+16(SP)\n ret\nRun Code Online (Sandbox Code Playgroud)\n然后自由内联。
\npackage lib\n\nfunc zeroOrOne(predicate bool) (result int) {\n if predicate {\n result = 1\n }\n return\n}\n\ntype Vendor1 struct {\n Property1 int\n Property2 float32\n Property3 bool\n}\n\n// Vendor2 bit positions.\nconst (\n Property1Bit = 2\n Property2Bit = 3\n Property3Bit = 5\n)\n\nfunc Convert1To2(v1 Vendor1) (result int) {\n result |= zeroOrOne(v1.Property1 == 1) << Property1Bit\n result |= zeroOrOne(v1.Property2 < 0.0) << Property2Bit\n result |= zeroOrOne(v1.Property3) << Property3Bit\n return\n}\nRun Code Online (Sandbox Code Playgroud)\n产生https://go.godbolt.org/z/eKbK17
\n movq "".v1+8(SP), AX\n cmpq AX, $1\n seteq AL\n xorps X0, X0\n movss "".v1+16(SP), X1\n ucomiss X1, X0\n sethi CL\n movblzx AL, AX\n shlq $2, AX\n movblzx CL, CX\n shlq $3, CX\n orq CX, AX\n movblzx "".v1+20(SP), CX\n shlq $5, CX\n orq AX, CX\n movq CX, "".result+24(SP)\n ret\nRun Code Online (Sandbox Code Playgroud)\n
如果您的所有分支都产生副作用或在计算上昂贵,则以下内容将在语义上进行重构:
index := func() int {
if val > 0 {
return printPositiveAndReturn(val)
} else {
return slowlyReturn(-val) // or slowlyNegate(val)
}
}(); # exactly one branch will be evaluated
Run Code Online (Sandbox Code Playgroud)
通常没有开销(内联),最重要的是,不会使用仅使用一次的辅助函数来使名称空间混乱(这会影响可读性和维护性)。现场例子
请注意,如果您要天真地应用Gustavo的方法:
index := printPositiveAndReturn(val);
if val <= 0 {
index = slowlyReturn(-val); // or slowlyNegate(val)
}
Run Code Online (Sandbox Code Playgroud)
你会得到一个行为不同的程序; 万一val <= 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)())
}
Run Code Online (Sandbox Code Playgroud)
输出
hello
hello world
hello world
hello
bye
Run Code Online (Sandbox Code Playgroud)
interface{}以满足内部强制转换操作。c。这里的独立解决方案也很好,但对于某些用途来说可能不太清楚。
现在随着 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
}
Run Code Online (Sandbox Code Playgroud)
请注意,如果在这种情况下使用它,它会崩溃。在这种情况下,只需使用 if 语句(因为您向函数传递一个 nil 指针,而 if 语句如果为 false,则不会调用该部分)
var a *string
fmt.Println(Ternary(a != nil, *a, "some thing else"))
Run Code Online (Sandbox Code Playgroud)
该解决方案使用函数调用它,因此如果为 false 则不会执行
func TernaryPointer[T any](condition bool, If, Else func() T) T {
if condition {
return If()
}
return Else()
}
Run Code Online (Sandbox Code Playgroud)
var pString *string
fmt.Println(TernaryPointer(
pString != nil, // condition
func() string { return *pString }, // true
func() string { return "new data" }, // false
))
Run Code Online (Sandbox Code Playgroud)
但在这种情况下,我认为常规的 if 语句更干净(除非 go 在将来添加箭头函数)
给予这个答案他已经回答过的信任
| 归档时间: |
|
| 查看次数: |
154218 次 |
| 最近记录: |