Як зробити один-лайнер, якщо інакше заява?


104

Чи можу я написати просту заяву 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, буває, має лише одного такого оператора. Ось чому його зазвичай називають потрійним оператором. Його справжня назва - «умовний оператор».
втор

Відповіді:


132

Як зазначалося в коментарях, Go не підтримує потрійний один лайнер. Найкоротша форма, яку я можу придумати, така:

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

Але, будь ласка, не робіть цього, це не варто, і це лише бентежить людей, які читають ваш код.


83
@thoroc Я б, напевно, уникав цього в реальному житті, оскільки це не інтуїтивний IMHO, не варто економити 2 рядки для меншої читабельності.
Not_a_Golfer

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

3
Це залежить, наскільки читабельність. Я думаю, що це читабельніше, але як і все інше, цим можна зловживати. Якщо вам потрібно оголосити змінну, яка не потрібна поза блоком if, я вважаю, що це переможець.
arjabbar

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

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

29

Я часто використовую таке:

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

в основному те саме, що і @ Not_a_Golfer, але використовуючи тип виводу .


4
З таким же недоліком: ви ускладнюєте розуміння при використанні асиметричного рішення для очевидно симетричної вимоги.
Вовк

2
І той самий недолік перетворення того, що повинно бути ліниво оціненою конструкцією, коли лише одна з двох гілок коли-небудь буде використана в ту, де завжди буде оцінюватися одна, іноді і обидві (у цьому випадку перша оцінка була надмірною)
брус

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

Я, як правило, плачу, коли відходи навмисно цикла процесора.
алесіосаві

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 є оператор?:

У Гоу немає потрійної форми. Ви можете використовувати наступне, щоб досягти того ж результату:

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

знайдена додаткова інформація, яка може зацікавити цю тему:


це також можливо в одному рядку: var c int; if a > b { c = a } else { c = b }? Але я б запропонував зберегти його в 5 рядках, щоб сформувати світлий блок для відпочинку читача;)
Вовк

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

надати max 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
цікавий підхід. Він може бути корисним у складніших випадках. :)
торок

1

Іноді я намагаюся використовувати анонімну функцію, щоб домогтися того, щоб визначення та присвоєння траплялися в одному рядку. як нижче:

a, b = 4, 8

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

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


0

Дуже схожа конструкція доступна мовою

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

*

тобто

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

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, це те, що немає псевдоніму для функції за замовчуванням нульовий параметр повернення за замовчуванням, тоді це було б набагато приємніше (подумайте, як ви оголошуєте літерали карти, масиву та зрізів з просто назвою типу).

Або навіть більш коротку версію, як щойно запропонував коментатор:

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)
веллотіс

1
Так, це було б ще коротше, передаючи лише функції. Потрібно лише один раз мати цю функцію в одній бібліотеці утиліт, і ви можете використовувати її через ваш код.
Луки Сумірний
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.