Comment faire une déclaration if else sur une seule ligne?


104

Puis-je écrire une simple instruction if-else avec une affectation de variable dans go (golang) comme je le ferais en php? Par exemple:

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

Actuellement, je dois utiliser ce qui suit:

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

Désolé, je ne me souviens pas du nom si cette instruction de contrôle et je n'ai pas pu trouver les informations sur le site ou via la recherche Google. : /


4
C'est ce qu'on appelle un opérateur ternaire ... et non, Go n'en a pas.
Simon Whitehead

6
Je crois que le mot que vous recherchez est "ternaire"
BenjaminRH


10
Juste pour clarifier, un opérateur ternaire est tout opérateur d'arité 3, c'est-à-dire tout opérateur qui lie 3 sous-expressions. Il se trouve que C n'a qu'un seul opérateur. C'est pourquoi il est généralement appelé l'opérateur ternaire. Son vrai nom est cependant "opérateur conditionnel".
thwd

Réponses:


132

Comme les commentaires l'ont mentionné, Go ne prend pas en charge les doublures ternaires. La forme la plus courte à laquelle je puisse penser est la suivante:

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

Mais s'il vous plaît ne faites pas cela, cela n'en vaut pas la peine et ne fera que dérouter les personnes qui liront votre code.


84
@thoroc En fait, j'éviterais probablement cela dans la vraie vie, car ce n'est pas intuitif à mon humble avis, cela ne vaut pas la peine de sauver 2 lignes pour moins de lisibilité.
Not_a_Golfer

14
@thoroc, veuillez écouter ce que Not_a_Golfer a dit. Vous devez vous efforcer d'écrire des logiciels maintenables, ne pas vous montrer. Les astuces sont chouettes mais le prochain gars qui lira votre code (vous y compris dans quelques mois / années) ne les appréciera pas.
kostix

3
Cela dépend, en ce qui concerne la lisibilité. Je pense que c'est plus lisible, mais comme toute autre chose, on peut en abuser. Si vous devez déclarer une variable qui n'est pas nécessaire en dehors du bloc if, alors je pense que c'est un gagnant.
arjabbar

@Not_a_Golfer ce n'est pas seulement la lisibilité par symétrie, le contraste structurel avec l'intention se trouve également dans le code compilé, parfois même avec un coût supplémentaire pour une initialisation redondante.
Wolf

5
Oh, joie, regardez combien Go a gagné en n'ayant pas d'opérateur ternaire. Outre les commentaires sur la lisibilité, vous avez modifié une construction à évaluation paresseuse dans laquelle une seule branche est exécutée vers une où la première est toujours exécutée et la seconde peut être exécutée en plus.
itsbruce

29

J'utilise souvent ce qui suit:

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

fondamentalement la même chose que @ Not_a_Golfer mais en utilisant l' inférence de type .


4
Avec le même inconvénient: vous compliquez la compréhension lorsque vous utilisez une solution asymétrique pour un besoin manifestement symétrique.
Wolf

2
Et le même inconvénient de transformer ce qui devrait être une construction évaluée paresseusement où une seule des deux branches sera jamais utilisée en une seule où une sera toujours évaluée, parfois les deux (dans ce cas, la première évaluation était redondante)
itsbruce

1
Selon le cas d'utilisation, cela peut encore être très utile. Par exemple, listeningPath := "production.some.com"; if DEBUG { listeningPath := "development.some.com" }même vitesse que le ternaire pour la production, et à mon avis assez bonne lisibilité.
Lévite

Je pleure généralement lorsque je gaspille intentionnellement le cycle du processeur.
alessiosavi

28

Comme les autres l'ont mentionné, Gone prend pas en charge les monoplaces ternaires. Cependant, j'ai écrit une fonction utilitaire qui pourrait vous aider à réaliser ce que vous voulez.

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

Voici quelques cas de test pour montrer comment vous pouvez l'utiliser

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

Pour le plaisir, j'ai écrit des fonctions utilitaires plus utiles telles que:

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

Si vous souhaitez utiliser l'un de ceux-ci, vous pouvez les trouver ici https://github.com/shomali11/util


12

Merci d'avoir indiqué la bonne réponse.

Je viens de consulter la FAQ de Golang (duh) et il est clairement indiqué que ce n'est pas disponible dans la langue:

Go a-t-il l'opérateur?:?

Il n'y a pas de forme ternaire dans Go. Vous pouvez utiliser les éléments suivants pour obtenir le même résultat:

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

informations supplémentaires trouvées qui pourraient être intéressantes sur le sujet:


c'est également possible en une seule ligne var c int; if a > b { c = a } else { c = b }:? Mais je suggérerais de le garder en 5 lignes pour former un bloc lumineux pour la récréation du lecteur;)
Wolf

7

Une façon possible de le faire en une seule ligne en utilisant une carte, simple je vérifie si a > bsi elle est truej'assigne cà aautreb

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

Cependant, cela semble incroyable, mais dans certains cas, ce n'est peut-être PAS la solution parfaite en raison de l'ordre d'évaluation. Par exemple, si je vérifie si un objet n'en nilobtient pas une propriété, regardez l'extrait de code suivant qui le fera panicen cas demyObj 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}

Parce que la carte sera créée et construite avant d'évaluer la condition, dans ce cas, myObj = nilcela paniquera simplement.

Sans oublier de mentionner que vous pouvez toujours faire les conditions en une seule ligne simple, vérifiez les points suivants:

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

4

Utiliser la fonction lambda au lieu de l'opérateur ternaire

Exemple 1

pour donner le max int

package main

func main() {

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

Exemple 2

Supposons que vous ayez cette must(err error)fonction pour gérer les erreurs et que vous souhaitiez l'utiliser lorsqu'une condition n'est pas remplie. (profitez-en sur 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
approche intéressante, Cela pourrait être utile dans des cas plus complexes. :)
thoroc

1

Parfois, j'essaie d'utiliser la fonction anonyme pour que la définition et l'attribution se produisent sur la même ligne. comme ci-dessous:

a, b = 4, 8

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

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


0

Une construction très similaire est disponible dans la langue

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

*

c'est à dire

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

0

Vous pouvez utiliser une fermeture pour cela:

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

Le seul reproche que j'ai avec la syntaxe de fermeture dans Go est qu'il n'y a pas d'alias pour la fonction de retour zéro paramètre zéro par défaut, alors ce serait beaucoup plus agréable (pensez à la façon dont vous déclarez les littéraux de carte, de tableau et de tranche avec juste un nom de type).

Ou même la version plus courte, comme un commentateur vient de le suggérer:

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

Vous auriez toujours besoin d'utiliser une fermeture si vous deviez donner des paramètres aux fonctions. Cela pourrait être évité dans le cas du passage de méthodes plutôt que de simples fonctions, je pense, où les paramètres sont la structure associée aux méthodes.


Version plus courtedoif(condition, dothis, dothat)
vellotis

1
Oui, ce serait encore plus court, ne passant que les fonctions. Il suffit d'avoir cette fonction une seule fois dans une bibliothèque utilitaire, et vous pouvez l'utiliser tout au long de votre code.
Louki Sumirniy
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.