web-dev-qa-db-fra.com

Quel est l'équivalent Go idiomatique de l'opérateur ternaire de C?

En C/C++ (et de nombreux langages de cette famille), un idiome commun pour déclarer et initialiser une variable en fonction d'une condition utilise l'opérateur conditionnel ternaire:

int index = val > 0 ? val : -val

Go n'a pas l'opérateur conditionnel. Quel est le moyen le plus idiomatique d'implémenter le même morceau de code que ci-dessus? Je suis venu à la solution suivante, mais cela semble assez prolixe

var index int

if val > 0 {
    index = val
} else {
    index = -val
}

Y a-t-il quelque chose de mieux?

234
Fabien

Comme indiqué (et sans surprise, espérons-le), utiliser if+else est en effet le moyen idiomatique de faire des conditions dans Go.

En plus du bloc de code var+if+else complet, cette orthographe est aussi souvent utilisée:

index := val
if val <= 0 {
    index = -val
}

et si vous avez un bloc de code assez répétitif, tel que l'équivalent de int value = a <= b ? a : b, vous pouvez créer une fonction pour le contenir:

func min(a, b int) int {
    if a <= b {
        return a
    }
    return b
}

...

value := min(a, b)

Le compilateur va intégrer de telles fonctions simples, il est donc rapide, plus clair et plus court.

195
Gustavo Niemeyer

No Go n'a pas d'opérateur ternaire, en utilisant la syntaxe si/else est la manière idiomatique: http://golang.org/ doc/faq # Does_Go_have_a_ternary_form

58
ishaaq

Supposons que vous ayez l'expression ternaire suivante (en C):

int a = test ? 1 : 2;

L’approche idiomatique de Go consisterait simplement à utiliser un bloc if:

var a int

if test {
  a = 1
} else {
  a = 2
}

Cependant, cela pourrait ne pas correspondre à vos besoins. Dans mon cas, j'avais besoin d'une expression en ligne pour un modèle de génération de code.

J'ai utilisé une fonction anonyme immédiatement évaluée:

a := func() int { if test { return 1 } else { return 2 } }()

Cela garantit que les deux branches ne sont pas évaluées également.

48
Peter Boyer

La carte ternaire est facile à lire sans parenthèses:

c := map[bool]int{true: 1, false: 0} [5 > 4]
33
user1212212
func Ternary(statement bool, a, b interface{}) interface{} {
    if statement {
        return a
    }
    return b
}

func Abs(n int) int {
    return Ternary(n >= 0, n, -n).(int)
}

Cela ne surperformera pas si/else et nécessite un casting mais fonctionne. FYI:

BenchmarkAbsTernary-8 100000000 18.8 ns/op

BenchmarkAbsIfElse-8 2000000000 0.27 ns/op

7
Phillip Dominy

Si tous vos branches produisent des effets secondaires ou sont coûteux en calculs , ce qui suit serait un refactoring préservant sémantiquement :

index := func() int {
    if val > 0 {
        return printPositiveAndReturn(val)
    } else {
        return slowlyReturn(-val)  // or slowlyNegate(val)
    }
}();  # exactly one branch will be evaluated

avec normalement pas de surcharge (en ligne) et, plus important encore, sans encombrer votre espace de noms de fonctions d'assistance utilisées une seule fois (ce qui nuit à la lisibilité et à la maintenance) . Exemple en direct

Notez si vous deviez appliquer naïvement approche de Gustavo :

    index := printPositiveAndReturn(val);
    if val <= 0 {
        index = slowlyReturn(-val);  // or slowlyNegate(val)
    }

vous obtiendrez un programme avec un comportement différent ; au cas où le programme val <= 0 afficherait une valeur non positive alors qu'il ne le devrait pas! (De manière analogue, si vous inversez les branches, vous introduisez une surcharge en appelant une fonction lente inutilement.)

5
eold

la réponse de eold est intéressante et créative, peut-être même intelligente.

Cependant, il serait recommandé de faire plutôt:

var index int
if val > 0 {
    index = printPositiveAndReturn(val)
} else {
    index = slowlyReturn(-val)  // or slowlyNegate(val)
}

Oui, les deux compilent essentiellement vers le même Assembly, mais ce code est beaucoup plus lisible que d'appeler une fonction anonyme juste pour renvoyer une valeur qui aurait pu être écrite dans la variable en premier lieu.

Fondamentalement, un code simple et clair est préférable à un code créatif.

En outre, tout code utilisant un littéral de carte n'est pas une bonne idée, car les cartes ne sont pas légères dans Go. Depuis Go 1.3, l'ordre d'itération aléatoire pour les petites cartes est garanti et, pour le faire respecter, il est devenu beaucoup moins efficace du point de vue de la mémoire pour les petites cartes.

En conséquence, créer et supprimer de nombreuses petites cartes demande beaucoup de temps et d’espace. J'avais un morceau de code qui utilisait une petite carte (deux ou trois clés, est probable, mais le cas d'utilisation courant était seulement une entrée) Mais le code était chien lent. Nous parlons d'au moins 3 ordres de grandeur plus lentement que le même code réécrit pour utiliser une clé à double découpage [index] => data [index] map. Et probablement était plus. Comme certaines opérations qui prenaient auparavant quelques minutes à exécuter ont commencé en quelques millisecondes.

3
Cassy Foesch