Что такое идиоматический 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)

Компилятор будет встроить такие простые функции, чтобы они были быстрыми, понятными и короткими.

Ответ 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