Come si fa l'istruzione one-liner if else?


104

Posso scrivere una semplice istruzione if-else con assegnazione di variabili in go (golang) come farei in php? Per esempio:

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

Attualmente devo usare quanto segue:

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

Spiacente, non ricordo il nome se questa dichiarazione di controllo e non sono riuscito a trovare le informazioni nel sito o tramite la ricerca su Google. : /


4
Si chiama operatore ternario ... e no, Go non ne ha uno.
Simon Whitehead

6
Credo che la parola che stai cercando sia "ternario"
BenjaminRH


10
Giusto per chiarire, un operatore ternario è qualsiasi operatore di arity 3, cioè qualsiasi operatore che lega 3 sottoespressioni. C sembra avere un solo operatore di questo tipo. Ecco perché di solito è chiamato operatore ternario. Il suo vero nome è "operatore condizionale", però.
giovedì

Risposte:


132

Come menzionato nei commenti, Go non supporta le battute ternarie. La forma più breve a cui riesco a pensare è questa:

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

Ma per favore non farlo, non ne vale la pena e confonderà solo le persone che leggono il tuo codice.


84
@thoroc Probabilmente lo eviterei nella vita reale, poiché IMHO non è intuitivo, non vale la pena salvare 2 righe per una minore leggibilità.
Not_a_Golfer

14
@thoroc, per favore ascolta cosa ha detto Not_a_Golfer. Dovresti sforzarti di scrivere software mantenibile, non metterti in mostra. I trucchi puliti sono chiari, ma il prossimo ragazzo che leggerà il tuo codice (incluso te tra un paio di mesi / anni) non li apprezzerà.
kostix

3
Dipende, per quanto riguarda la leggibilità. Penso che questo sia più leggibile, ma come qualsiasi altra cosa, può essere abusato. Se hai bisogno di dichiarare una variabile che non è necessaria al di fuori del blocco if, allora penso che questo sia un vincitore.
arjabbar

@Not_a_Golfer non è solo leggibilità per simmetria, il contrasto strutturale con l'intento è anche nel codice compilato, a volte anche con un costo aggiuntivo per un'inizializzazione ridondante.
Wolf

5
Oh, gioia, guarda quanto guadagnava Go non avendo un operatore ternario. A parte i commenti sulla leggibilità fatti, hai cambiato un costrutto valutato pigramente in cui solo un ramo viene eseguito in uno in cui il primo viene sempre eseguito e il secondo può essere eseguito in aggiunta.
itsbruce

29

Uso spesso quanto segue:

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

fondamentalmente lo stesso di @ Not_a_Golfer ma usando l' inferenza del tipo .


4
Con lo stesso difetto: si complica la comprensione quando si usa una soluzione asimmetrica per un requisito ovviamente simmetrico.
Wolf

2
E lo stesso difetto di trasformare quello che dovrebbe essere un costrutto valutato pigramente in cui solo uno dei due rami sarà mai utilizzato in uno in cui uno sarà sempre valutato, a volte entrambi (nel qual caso la prima valutazione era ridondante)
primo

1
A seconda del caso d'uso, questo potrebbe essere ancora molto utile. Ad esempio, la listeningPath := "production.some.com"; if DEBUG { listeningPath := "development.some.com" }stessa velocità del ternario per la produzione e una buona leggibilità.
Levita

Di solito piango quando spreco intenzionalmente il ciclo della CPU.
alessiosavi

28

Come gli altri menzionati, Gonon supporta le battute ternarie. Tuttavia, ho scritto una funzione di utilità che potrebbe aiutarti a ottenere ciò che desideri.

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

Ecco alcuni casi di test per mostrare come puoi usarlo

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

Per divertimento, ho scritto funzioni di utilità più utili come:

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

Se desideri utilizzare uno di questi, puoi trovarli qui https://github.com/shomali11/util


12

Grazie per aver indicato la risposta corretta.

Ho appena controllato le FAQ di Golang (duh) e afferma chiaramente che non è disponibile nella lingua:

Go ha l'operatore?:?

Non esiste una forma ternaria in Go. È possibile utilizzare quanto segue per ottenere lo stesso risultato:

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

ulteriori informazioni trovate che potrebbero essere di interesse sull'argomento:


è anche possibile in una riga var c int; if a > b { c = a } else { c = b }:? Ma suggerirei di tenerlo in 5 righe per formare un blocco luminoso per la ricreazione del lettore;)
Wolf

7

Un modo possibile per fare questo in una sola linea utilizzando una mappa, semplice Sto controllando se a > bse è trueSto assegnando cal acontrariob

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

Tuttavia, sembra incredibile ma in alcuni casi potrebbe NON essere la soluzione perfetta a causa dell'ordine di valutazione. Ad esempio, se sto controllando se un oggetto non nilottiene una proprietà da esso, guarda il seguente frammento di codice che panicin caso dimyObj 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}

Perché la mappa verrà creata e costruita prima di valutare la condizione, quindi in questo caso myObj = nilandrà semplicemente nel panico.

Per non dimenticare di menzionare che puoi ancora eseguire le condizioni in una sola semplice riga, controlla quanto segue:

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

4

Usa la funzione lambda invece dell'operatore ternario

Esempio 1

per dare il massimo int

package main

func main() {

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

Esempio 2

Supponiamo di avere questa must(err error)funzione per gestire gli errori e di volerla utilizzare quando una condizione non è soddisfatta. (divertiti su 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
approccio interessante, potrebbe essere utile in casi più complessi. :)
thoroc

1

A volte, provo a utilizzare la funzione anonima per ottenere che la definizione e l'assegnazione avvengano sulla stessa linea. come sotto:

a, b = 4, 8

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

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


0

Una costruzione molto simile è disponibile nella lingua

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

*

cioè

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

0

Puoi usare una chiusura per questo:

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

L'unico inconveniente che ho con la sintassi di chiusura in Go è che non esiste un alias per la funzione di ritorno zero del parametro zero predefinito, quindi sarebbe molto più carino (pensa come dichiari map, array e slice letterals con solo un nome di tipo).

O anche la versione più breve, come ha appena suggerito un commentatore:

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

Avresti comunque bisogno di usare una chiusura se avessi bisogno di dare parametri alle funzioni. Ciò potrebbe essere ovviato nel caso di passaggio di metodi piuttosto che di funzioni, credo, dove i parametri sono la struttura associata ai metodi.


Versione più brevedoif(condition, dothis, dothat)
vellotis

1
Sì, sarebbe ancora più breve, passando solo le funzioni. Basta avere questa funzione una sola volta in una libreria di utilità e puoi usarla tutta attraverso il tuo codice.
Louki Sumirniy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.