Что такое идиоматический Go эквивалент C-тернарного оператора?
В C/С++ (и на многих языках этого семейства) общая идиома для объявления и инициализации переменной в зависимости от условия использует тернарный условный оператор:
int index = val > 0 ? val : -val
Go не имеет условного оператора. Каков самый идиоматический способ реализации того же кода, что и выше? Я пришел к следующему решению, но он выглядит довольно многословным
var index int
if val > 0 {
index = val
} else {
index = -val
}
Есть ли что-то лучше?
Ответы
Ответ 1
Как указано (и, мы надеемся, неудивительно) использование 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)
Компилятор будет встроить такие простые функции, чтобы они были быстрыми, понятными и короткими.
Ответ 2
Нет Go не имеет тройного оператора, используя if/else синтаксис идиоматический путь: http://golang.org/doc/faq#Does_Go_have_a_ternary_form
Ответ 3
Предположим, что у вас есть следующее тернарное выражение (в C):
int a = test ? 1 : 2;
Идиоматическим подходом в Go было бы просто использовать блок if
:
var a int
if test {
a = 1
} else {
a = 2
}
Однако это может не соответствовать вашим требованиям. В моем случае мне понадобилось встроенное выражение для шаблона генерации кода.
Я использовал сразу оцененную анонимную функцию:
a := func() int { if test { return 1 } else { return 2 } }()
Это гарантирует, что обе ветки также не будут оцениваться.
Ответ 4
Тарнар карты легко читается без круглых скобок:
c := map[bool]int{true: 1, false: 0} [5 > 4]
Ответ 5
Если все ваши ветки совершают побочные эффекты или являются дорогостоящими с точки зрения вычислительной техники, следующий рефакторинг, сохраняющий семантику:
index := func() int {
if val > 0 {
return printPositiveAndReturn(val)
} else {
return slowlyReturn(-val) // or slowlyNegate(val)
}
}(); # exactly one branch will be evaluated
обычно без накладных расходов (в строках) и, самое главное, без загромождения вашего пространства имен со вспомогательными функциями, которые используются только один раз (что затрудняет чтение и обслуживание). Живой пример
Обратите внимание, если бы вы наивно применяли метод Gustavo:
index := printPositiveAndReturn(val);
if val <= 0 {
index = slowlyReturn(-val); // or slowlyNegate(val)
}
вы получите программу с другим поведением; в случае, если программа val <= 0
напечатала неположительное значение, пока оно не должно! (Аналогично, если вы отменили ветки, вы должны ввести накладные расходы, вызвав медленную функцию без необходимости.)
Ответ 6
Ответ на интерес интересен и творчен, возможно, даже умный.
Однако было бы желательно сделать следующее:
var index int
if val > 0 {
index = printPositiveAndReturn(val)
} else {
index = slowlyReturn(-val) // or slowlyNegate(val)
}
Да, они оба скомпилируются по существу на одну и ту же сборку, однако этот код гораздо более разборчив, чем вызов анонимной функции, чтобы вернуть значение, которое могло быть записано в эту переменную в первую очередь.
В принципе, простой и понятный код лучше, чем творческий код.
Кроме того, любой код с использованием символа карты не является хорошей идеей, потому что карты вообще не легки в Go. Начиная с Go 1.3, случайный порядок итераций для малых карт гарантирован, и для обеспечения этого он получил немного менее эффективную память для небольших карт.
В результате, создание и удаление многочисленных небольших карт является как пространственным, так и трудоемким. У меня был фрагмент кода, который использовал небольшую карту (два или три ключа, скорее всего, но обычный вариант использования был только одной записью). Но код был медленным. Мы говорим, по крайней мере, на 3 порядка медленнее, чем тот же код, который был переписан для использования карты двойного среза [index] = > data [index]. И, скорее всего, было больше. Поскольку некоторые операции, которые ранее выполнялись за пару минут, запускались в миллисекундах.\
Ответ 7
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 и требует отбрасывания, но работает. FYI:
BenchmarkAbsTernary-8 100000000 18.8 ns/op
BenchmarkAbsIfElse-8 2000000000 0,27 ns/op