Jak zrobić jedno-liniowe oświadczenie if else?

104

Czy mogę napisać prostą instrukcję if-else z przypisaniem zmiennej w go (golang), tak jak zrobiłbym to w php? Na przykład:

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

Obecnie muszę korzystać z:

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

Przepraszamy, nie pamiętam nazwy, jeśli to oświadczenie kontrolne i nie mogłem znaleźć informacji w witrynie lub w wyszukiwarce Google. : /

thoroc
źródło
4
Nazywa się operatorem trójskładnikowym ... i nie, Go nie ma.
Simon Whitehead,
6
Uważam, że słowo, którego szukasz, to „trójskładnik”
BenjaminRH
10
Dla wyjaśnienia, operator trójskładnikowy to dowolny operator arity 3, czyli dowolny operator wiążący 3 wyrażenia podrzędne. C ma tylko jednego takiego operatora. Dlatego jest zwykle nazywany operatorem trójskładnikowym. Jednak jego prawdziwa nazwa to „operator warunkowy”.
czwartek

Odpowiedzi:

132

Jak wspomniano w komentarzach, Go nie obsługuje trójskładnikowych wkładek. Najkrótsza forma, jaką przychodzi mi do głowy, to:

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

Ale proszę, nie rób tego, to nie jest tego warte i tylko zmyli ludzi, którzy przeczytają Twój kod.

Not_a_Golfer
źródło
84
@thoroc prawdopodobnie uniknąłbym tego w prawdziwym życiu, ponieważ jest to nieintuicyjne IMHO, nie warto zapisywać 2 wierszy dla mniejszej czytelności.
Not_a_Golfer
14
@thoroc, posłuchaj, co powiedział Not_a_Golfer. Powinieneś starać się pisać łatwe w utrzymaniu oprogramowanie, a nie popisywać się. Zgrabne sztuczki są fajne, ale następny facet czytający Twój kod (w tym Ciebie za kilka miesięcy / lat) ich nie doceni.
kostix
3
To zależy od czytelności. Myślę, że jest to bardziej czytelne, ale jak wszystko inne, może być nadużywane. Jeśli chcesz zadeklarować zmienną, która nie jest potrzebna poza blokiem if, myślę, że jest to zwycięzca.
arjabbar
@Not_a_Golfer to nie tylko czytelność dzięki symetrii, strukturalny kontrast z intencją jest również w skompilowanym kodzie, czasem nawet z dodatkowym kosztem redundantnej inicjalizacji.
Wolf
5
Och, radość, spójrz, ile Go zyskało, nie mając trójskładnikowego operatora. Oprócz uwag dotyczących czytelności, zmieniłeś leniwie ocenianą konstrukcję, w której tylko jedna gałąź jest kiedykolwiek uruchamiana na taką, w której pierwsza jest zawsze uruchamiana, a druga może być uruchamiana dodatkowo.
itsbruce
29

Często używam następujących:

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

w zasadzie to samo, co @ Not_a_Golfer, ale przy użyciu wnioskowania o typie .

xojuz
źródło
4
Z tą samą wadą: utrudniasz zrozumienie, używając rozwiązania asymetrycznego dla oczywiście symetrycznego wymagania.
Wolf
2
I ta sama wada polegająca na przekształceniu tego, co powinno być leniwie ocenianym konstruktem, w którym tylko jedna z dwóch gałęzi będzie kiedykolwiek używana w jedną, w której zawsze będzie oceniana, czasami obie (w takim przypadku pierwsza ocena była zbędna)
brzuszek
1
W zależności od przypadku użycia może to być nadal bardzo przydatne. Np. Taka listeningPath := "production.some.com"; if DEBUG { listeningPath := "development.some.com" }sama prędkość jak trójskładnik dla produkcji i imho całkiem niezła czytelność.
Levite
Zwykle płaczę, gdy marnuje się celowo cykl procesora.
alessiosavi
28

Jak wspomnieli inni, Gonie obsługuje trójskładnikowych jednowarstwowych. Jednak napisałem funkcję narzędzia, która może pomóc ci osiągnąć to, czego chcesz.

// 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
}

Oto kilka przypadków testowych, które pokazują, jak możesz z niego korzystać

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)
}

Dla zabawy napisałem bardziej przydatne funkcje narzędziowe, takie jak:

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

Jeśli chciałbyś użyć któregokolwiek z nich, możesz je znaleźć tutaj https://github.com/shomali11/util

Raed Shomali
źródło
12

Dzięki za wskazanie poprawnej odpowiedzi.

Właśnie sprawdziłem Golang FAQ (duh) i wyraźnie stwierdza, że ​​nie jest to dostępne w języku:

Czy Go ma operatora?:?

W Go nie ma formy trójskładnikowej. Aby osiągnąć ten sam rezultat, możesz skorzystać z następujących metod:

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

znaleziono dodatkowe informacje, które mogą być interesujące na ten temat:

thoroc
źródło
jest to również możliwe w jednej linii var c int; if a > b { c = a } else { c = b }:? Ale sugerowałbym trzymanie go w 5 liniach, aby stworzyć lekki blok dla rekreacji czytelnika;)
Wolf
7

Jednym z możliwych sposobów, aby to zrobić tylko w jednym wierszu za pomocą mapy, proste sprawdzanie, czy jestem a > b, czy to trueja jestem przypisanie cdo ainaczejb

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

Wygląda to jednak niesamowicie, ale w niektórych przypadkach może NIE być idealnym rozwiązaniem ze względu na kolejność oceny. Na przykład, jeśli sprawdzam, czy obiekt nie niluzyskuje z niego jakiejś właściwości, spójrz na następujący fragment kodu, który będzie panicw przypadkumyObj 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}

Ponieważ mapa zostanie utworzona i zbudowana jako pierwsza przed oceną stanu, więc w takim przypadku myObj = nilpo prostu wpadnie w panikę.

Nie zapomnij wspomnieć, że nadal możesz wykonać warunki w jednej prostej linii, sprawdź następujące elementy:

var c int
...
if a > b { c = a } else { c = b}
Muhammad Soliman
źródło
4

Użyj funkcji lambda zamiast operatora trójargumentowego

Przykład 1

aby podać max int

package main

func main() {

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

Przykład 2

Załóżmy, że masz tę must(err error)funkcję do obsługi błędów i chcesz jej użyć, gdy warunek nie jest spełniony. (ciesz się na 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)
}
Alice Vixie
źródło
2
ciekawe podejście, może być przydatne w bardziej złożonych przypadkach. :)
thoroc
1

Czasami próbuję użyć funkcji anonimowej, aby definiowanie i przypisywanie odbywało się w tej samej linii. jak poniżej:

a, b = 4, 8

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

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

Ron
źródło
0

Bardzo podobna konstrukcja jest dostępna w języku

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

*

to znaczy

if path,err := os.Executable(); err != nil {
   log.Println(err)
} else {
   log.Println(path)
}
user2680100
źródło
0

Możesz użyć do tego zamknięcia:

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() })
}

Jedyne zastrzeżenie, jakie mam ze składnią zamknięcia w Go, to brak aliasu dla domyślnej funkcji zwracającej zero parametru zero, wtedy byłoby o wiele ładniej (pomyśl jak deklarujesz literały mapy, tablicy i wycinka z samą nazwą typu).

Albo nawet krótsza wersja, jak właśnie zasugerował komentator:

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)
}

Nadal będziesz musiał użyć domknięcia, jeśli chcesz podać parametry funkcji. Można by tego uniknąć w przypadku przekazywania metod, a nie tylko funkcji, jak myślę, gdzie parametry są strukturą związaną z metodami.

Louki Sumirniy
źródło
Krótsza wersjadoif(condition, dothis, dothat)
vellotis
1
Tak, to byłoby jeszcze krótsze, przekazując tylko funkcje. Wystarczy mieć tę funkcję tylko raz w jednej bibliotece narzędziowej i możesz jej używać w całym kodzie.
Louki Sumirniy