Как сделать однострочный оператор if else?


106

Могу ли я написать простой оператор if-else с присвоением переменных в go (golang), как если бы я делал это в php? Например:

$var = ( $a > $b )? $a: $b;

В настоящее время мне нужно использовать следующее:

var c int
if a > b {
    c = a
} else {
    c = b
}

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


4
Это называется тернарным оператором ... и нет, в Go его нет.
Саймон Уайтхед

6
Я считаю, что вы ищете слово «тройной»
BenjaminRH


10
Чтобы уточнить, тернарный оператор - это любой оператор арности 3, то есть любой оператор, связывающий 3 подвыражения. В C есть только один такой оператор. Вот почему его обычно называют тернарным оператором. Однако настоящее его название - «условный оператор».
thwd

Ответы:


135

Как уже упоминалось в комментариях, Go не поддерживает тройные однострочники. Самая короткая форма, которую я могу придумать, такова:

var c int
if c = b; a > b {
    c = a
}

Но, пожалуйста, не делайте этого, это того не стоит и только запутает людей, читающих ваш код.


84
@thoroc Я бы, вероятно, избегал этого в реальной жизни, поскольку это не интуитивно понятно, ИМХО, не стоит сохранять 2 строки для меньшей читабельности.
Not_a_Golfer

14
@thoroc, пожалуйста, послушайте, что сказал Not_a_Golfer. Вам следует стремиться писать поддерживаемое программное обеспечение, а не выставлять напоказ. Изящные уловки хороши, но следующий парень, читающий ваш код (включая вас через пару месяцев / лет), их не оценит.
kostix

3
Смотря насколько читабельнее. Я думаю, что это более читабельно, но, как и все остальное, им можно злоупотреблять. Если вам нужно объявить переменную, которая не нужна вне блока if, я думаю, что это победитель.
arjabbar 09

@Not_a_Golfer - это не только удобочитаемость по симметрии, структурный контраст с намерением также присутствует в скомпилированном коде, иногда даже с дополнительной стоимостью избыточной инициализации.
Wolf

5
О, радость, посмотри, сколько Go выиграл, не имея тернарного оператора. Помимо сделанных комментариев по удобочитаемости, вы изменили конструкцию с ленивым вычислением, в которой когда-либо запускается только одна ветвь, на ту, где первая всегда запускается, а вторая может запускаться дополнительно.
itsbruce 01

30

Я часто использую следующее:

c := b
if a > b {
    c = a
}

в основном то же самое, что и @ Not_a_Golfer, но с использованием вывода типа .


4
С тем же недостатком: вы усложняете понимание при использовании асимметричного решения для явно симметричного требования.
Wolf

2
И тот же недостаток превращения того, что должно быть конструкцией с отложенным вычислением, где только одна из двух ветвей будет когда-либо использоваться, в одну, где всегда будет оцениваться одна, иногда обе (в этом случае первая оценка была избыточной)
itsbruce

1
В зависимости от варианта использования это может быть очень полезно. Например, listeningPath := "production.some.com"; if DEBUG { listeningPath := "development.some.com" }та же скорость, что и у троичного для продакшена, и, имхо, довольно хорошая читаемость.
Левит

Я обычно плачу, когда намеренно трачу цикл процессора.
alessiosavi

28

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

// IfThenElse evaluates a condition, if true returns the first parameter otherwise the second
func IfThenElse(condition bool, a interface{}, b interface{}) interface{} {
    if condition {
        return a
    }
    return b
}

Вот несколько тестовых примеров, чтобы показать, как вы можете его использовать

func TestIfThenElse(t *testing.T) {
    assert.Equal(t, IfThenElse(1 == 1, "Yes", false), "Yes")
    assert.Equal(t, IfThenElse(1 != 1, nil, 1), 1)
    assert.Equal(t, IfThenElse(1 < 2, nil, "No"), nil)
}

Ради интереса я написал более полезные служебные функции, такие как:

IfThen(1 == 1, "Yes") // "Yes"
IfThen(1 != 1, "Woo") // nil
IfThen(1 < 2, "Less") // "Less"

IfThenElse(1 == 1, "Yes", false) // "Yes"
IfThenElse(1 != 1, nil, 1)       // 1
IfThenElse(1 < 2, nil, "No")     // nil

DefaultIfNil(nil, nil)  // nil
DefaultIfNil(nil, "")   // ""
DefaultIfNil("A", "B")  // "A"
DefaultIfNil(true, "B") // true
DefaultIfNil(1, false)  // 1

FirstNonNil(nil, nil)                // nil
FirstNonNil(nil, "")                 // ""
FirstNonNil("A", "B")                // "A"
FirstNonNil(true, "B")               // true
FirstNonNil(1, false)                // 1
FirstNonNil(nil, nil, nil, 10)       // 10
FirstNonNil(nil, nil, nil, nil, nil) // nil
FirstNonNil()                        // nil

Если вы хотите использовать любой из них, вы можете найти их здесь https://github.com/shomali11/util


12

Спасибо, что указали на правильный ответ.

Я только что проверил FAQ Golang (да), и там ясно сказано, что это недоступно на языке:

Есть ли в Go оператор ?:?

В Go нет троичной формы. Для достижения того же результата вы можете использовать следующее:

if expr {
    n = trueVal
} else {
    n = falseVal
}

обнаружена дополнительная информация, которая может быть интересна по теме:


это также возможно в одной строке var c int; if a > b { c = a } else { c = b }:? Но я бы посоветовал оставить его в 5 строк, чтобы сформировать световой блок для отдыха читателя;)
Wolf

7

Один из возможных способов сделать это только в одной строке, используя карту, просто я проверяю ли a > bесли trueя назначаю cв aпротивном случаеb

c := map[bool]int{true: a, false: b}[a > b]

Однако это выглядит потрясающе, но в некоторых случаях это может быть НЕ идеальное решение из-за порядка оценки. Например, если я проверяю, не nilполучает ли объект какое-либо свойство из него, посмотрите следующий фрагмент кода, который panicв случаеmyObj equals nil

type MyStruct struct {
   field1 string
   field2 string 
}

var myObj *MyStruct
myObj = nil 

myField := map[bool]string{true: myObj.field1, false: "empty!"}[myObj != nil}

Поскольку карта будет создана и построена сначала до оценки состояния, поэтому в случае myObj = nilэтого просто произойдет паника.

Не забудьте упомянуть, что вы все еще можете выполнять условия в одной простой строке, проверьте следующее:

var c int
...
if a > b { c = a } else { c = b}

4

Используйте лямбда-функцию вместо тернарного оператора

Пример 1

чтобы дать максимальное int

package main

func main() {

    println( func(a,b int) int {if a>b {return a} else {return b} }(1,2) )
}

Пример 2

Предположим, у вас есть эта must(err error)функция для обработки ошибок и вы хотите использовать ее, когда условие не выполняется. (наслаждайтесь на https://play.golang.com/p/COXyo0qIslP )

package main

import (
    "errors"
    "log"
    "os"
)

// must is a little helper to handle errors. If passed error != nil, it simply panics.
func must(err error) {
    if err != nil {
        log.Println(err)
        panic(err)
    }
}

func main() {

    tmpDir := os.TempDir()
    // Make sure os.TempDir didn't return empty string
    // reusing my favourite `must` helper
    // Isn't that kinda creepy now though?
    must(func() error {
        var err error
        if len(tmpDir) > 0 {
            err = nil
        } else {
            err = errors.New("os.TempDir is empty")
        }
        return err
    }()) // Don't forget that empty parentheses to invoke the lambda.
    println("We happy with", tmpDir)
}

2
интересный подход, может быть полезен в более сложных случаях. :)
Thoroc

1

Очень похожая конструкция доступна на языке

**if <statement>; <evaluation> {
   [statements ...]
} else {
   [statements ...]
}*

*

т.е.

if path,err := os.Executable(); err != nil {
   log.Println(err)
} else {
   log.Println(path)
}

1

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

a, b = 4, 8

c := func() int {
    if a >b {
      return a
    } 
    return b
  } ()

https://play.golang.org/p/rMjqytMYeQ0


0

Для этого можно использовать закрытие:

func doif(b bool, f1, f2 func()) {
    switch{
    case b:
        f1()
    case !b:   
        f2()
    }
}

func dothis() { fmt.Println("Condition is true") }

func dothat() { fmt.Println("Condition is false") }

func main () {
    condition := true
    doif(condition, func() { dothis() }, func() { dothat() })
}

Единственная проблема, с которой я столкнулся с синтаксисом закрытия в Go, - это отсутствие псевдонима для функции возврата с нулевым параметром по умолчанию, тогда это было бы намного лучше (подумайте, как вы объявляете литералы map, array и slice только с именем типа).

Или даже более короткая версия, как только что предложил комментатор:

func doif(b bool, f1, f2 func()) {
    switch{
    case b:
        f1()
    case !b:   
        f2()
    }
}

func dothis() { fmt.Println("Condition is true") }

func dothat() { fmt.Println("Condition is false") }

func main () {
    condition := true
    doif(condition, dothis, dothat)
}

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


Укороченная версияdoif(condition, dothis, dothat)
vellotis

1
Да, это было бы еще короче, передав только функции. Эту функцию нужно иметь только один раз в одной служебной библиотеке, и вы можете использовать ее в своем коде.
Луки Сумирный
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.