Что такое идиоматический эквивалент троичного оператора C?


в C/C++ (и многих языках этого семейства) общая идиома для объявления и инициализации переменной в зависимости от условия использует тернарный условный оператор :

int index = val > 0 ? val : -val

Go не имеет условного оператора. Каков наиболее идиоматический способ реализации того же фрагмента кода, что и выше ? Я пришел к следующему решению, но оно кажется довольно многословным

var index int

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

есть ли что-то лучше ?

8 165

8 ответов:

как указано (и, надеюсь, неудивительно), используя 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)

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

No Go не имеет тернарного оператора, используя синтаксис if / else и идиоматический Способ:http://golang.org/doc/faq#Does_Go_have_a_ternary_form

предположим, что у вас есть следующее тернарное выражение (в 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 } }()

это гарантирует, что обе ветви также не оцениваются.

тернарная карта легко читается без скобок:

c := map[bool]int{true: 1, false: 0} [5 > 4]
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 и требует приведения, но работает. К вашему сведению:

BenchmarkAbsTernary-8 100000000 18.8 ns / op

BenchmarkAbsIfElse-8 2000000000 0.27 ns / op

Если все ваши филиалы сделать побочные эффекты или ресурсоемким следующая будет семантически-сохранение рефакторинг:

index := func() int {
    if val > 0 {
        return printPositiveAndReturn(val)
    } else {
        return slowlyReturn(-val)  // or slowlyNegate(val)
    }
}();  # exactly one branch will be evaluated

обычно без накладных расходов( встроенных) и, самое главное, без загромождения вашего пространства имен вспомогательными функциями, которые использовать только один раз (что затрудняет читаемость и техническое обслуживание). Видео

обратите внимание, если вы надо было наивно подать заявку Густаво:

    index := printPositiveAndReturn(val);
    if val <= 0 {
        index = slowlyReturn(-val);  // or slowlyNegate(val)
    }

вы получите программу с другое поведение; в случае val <= 0 программа будет печатать неположительное значение, пока оно не должно! (Аналогично, если вы перевернули ветви, вы бы ввели накладные расходы, вызывая медленную функцию без необходимости.)

ответ элда интересен и креативен, возможно, даже умен.

однако, было бы рекомендовал вместо этого:

var index int
if val > 0 {
    index = printPositiveAndReturn(val)
} else {
    index = slowlyReturn(-val)  // or slowlyNegate(val)
}

Да, они оба компилируются до по существу одной и той же сборки, однако этот код гораздо более разборчив, чем вызов анонимной функции только для того, чтобы вернуть значение, которое могло быть записано в переменную в первую очередь.

в принципе, простой и понятный код лучше, чем творческий код.

кроме того, любой код, использующий литерал карты, не является хорошей идеей, потому что карты не являются легкими вообще в Go. Поскольку Go 1.3, случайный порядок итераций для небольших карт гарантирован, и для обеспечения этого он получил довольно мало эффективной памяти для небольших карт.

в результате создание и удаление многочисленных небольших карт занимает много места и времени. У меня был кусок кода, который использовал небольшую карту (два или три ключа, вероятно, но общий случай использования была только одна запись), но код был собака медленно. Мы говорим по крайней мере на 3 Порядка медленнее, чем тот же код, переписанный для использования двойного ключа среза[index]=>Data[index] map. И скорее всего было больше. Поскольку некоторые операции, которые ранее занимали пару минут для запуска, начали завершаться в миллисекундах.\

/*
* type ListNode struct {
*     Val int
*     Next *ListNode
* }
*/

val := (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carry