563 Stimmen

Was ist das idiomatische Go-Äquivalent des C-Ternäroperators?

In C/C++ (und vielen Sprachen dieser Familie) wird in einem häufigen Idiom zur Deklaration und Initialisierung einer Variablen je nach Bedingung der bedingte ternäre Operator verwendet:

int index = val > 0 ? val : -val

Go hat den bedingten Operator nicht. Wie lässt sich das oben genannte Code-Stück auf die idiomatischste Weise implementieren? Ich kam zu folgender Lösung, aber sie erscheint ziemlich umständlich

var index int

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

Gibt es etwas Besseres?

458voto

Gustavo Niemeyer Punkte 21529

Wie bereits erwähnt (und hoffentlich nicht überraschend) ist die Verwendung von if+else in der Tat die idiomatische Art, Bedingungen in Go zu verwenden.

Zusätzlich zum vollständigen var+if+else Block Code wird auch oft diese Schreibweise verwendet:

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

Und wenn Sie einen Block Code haben, der häufig genug wiederholt wird, wie zum Beispiel das Äquivalent von int value = a <= b ? a : b, können Sie eine Funktion erstellen, um dies zu speichern:

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

...

value := min(a, b)

Der Compiler wird solche einfachen Funktionen inlinen, daher ist es schnell, klarer und kürzer.

169voto

ishaaq Punkte 5825

Nein, Go hat keinen ternären Operator. Die Verwendung der if/else-Syntax ist der idiomatische Weg.

Warum hat Go nicht den ?: Operator?

Es gibt keine ternäre Testoperation in Go. Sie können das folgende verwenden, um das gleiche Ergebnis zu erzielen:

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

Der Grund, warum ?: in Go fehlt, ist, dass die Designer der Sprache die Operation zu oft verwendet sahen, um undurchsichtige komplexe Ausdrücke zu erstellen. Die if-else-Form ist zwar länger, aber zweifellos klarer. Eine Sprache braucht nur ein bedingtes Steuerungsflusskonstrukt.

— Häufig gestellte Fragen (FAQ) - Die Go-Programmiersprache

89voto

Peter Boyer Punkte 997

Angenommen, Sie haben den folgenden ternären Ausdruck (in C):

int a = test ? 1 : 2;

Der idiomatische Ansatz in Go wäre die Verwendung eines if Block:

var a int

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

Das könnte jedoch nicht Ihren Anforderungen entsprechen. In meinem Fall benötigte ich einen Inline-Ausdruck für eine Codegenerierungsvorlage.

Ich habe eine sofort ausgewertete anonyme Funktion verwendet:

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

Dies stellt sicher, dass beide Zweige auch nicht evaluieren werden.

66voto

user1212212 Punkte 1193

Die karte ternär ist leicht zu lesen, ohne Klammern:

c := map[bool]int{true: 1, false: 0} [5 > 4]

65voto

icza Punkte 335148

Vorwort: Ohne zu argumentieren, dass if else der richtige Weg ist, können wir trotzdem mit Sprachkonstrukten spielen und Vergnügen finden.

Go 1.18 Generics Update: Go 1.18 fügt Generics-Unterstützung hinzu. Es ist jetzt möglich, eine generische If() Funktion wie diese zu erstellen. Hinweis: Dies ist verfügbar in github.com/icza/gog, als gog.If() (Offenlegung: Ich bin der Autor).

func If[T any](cond bool, vtrue, vfalse T) T {
    if cond {
        return vtrue
    }
    return vfalse
}

Welche du verwenden kannst like this:

min := If(i > 0, i, 0)

Die Antwort vor 1.18 lautet:


Das folgende If Konstrukt ist verfügbar in meiner github.com/icza/gox Bibliothek mit vielen anderen Methoden, als der gox.If Typ.


Go ermöglicht es, Methoden an beliebige benutzerdefinierte Typen anzuhängen, einschließlich primitiver Typen wie bool. Wir können einen benutzerdefinierten Typ erstellen, der bool als seinen unterliegenden Typ hat, und dann mit einer einfachen Typ Konvertierung auf die Bedingung haben wir Zugriff auf seine Methoden. Methoden, die die Operanden erhalten und auswählen.

So etwas wie dies:

type If bool

func (c If) Int(a, b int) int {
    if c {
        return a
    }
    return b
}

Wie können wir es nutzen?

i := If(condition).Int(val1, val2)  // Kurze Variablendeklaration, i ist vom Typ int
     |-----------|  \
   Typ-Konvertierung   \---Methodenaufruf

Zum Beispiel ein Ternärer max() machen:

i := If(a > b).Int(a, b)

Ein Ternärer abs() machen:

i := If(a >= 0).Int(a, -a)

Dies sieht cool aus, es ist einfach, elegant und effizient (es ist auch für die Inline-Ausführung geeignet).

Ein Nachteil im Vergleich zu einem "echten" ternären Operator: Es werden immer alle Operanden ausgewertet.

Um eine verzögerte und nur bei Bedarf erfolgende Auswertung zu erreichen, ist die einzige Option die Verwendung von Funktionen (entweder deklarierte Funktionen oder Methoden, oder Funktionsliterale), die nur aufgerufen werden, wenn / wenn sie benötigt werden:

func (c If) Fint(fa, fb func() int) int {
    if c {
        return fa()
    }
    return fb()
}

Verwendung: Nehmen wir an, wir haben diese Funktionen zur Berechnung von a und b:

func calca() int { return 3 }
func calcb() int { return 4 }

Dann:

i := If(someCondition).Fint(calca, calcb)

Zum Beispiel die Bedingung, dass das aktuelle Jahr > 2020 ist:

i := If(time.Now().Year() > 2020).Fint(calca, calcb)

Wenn wir Funktionenliterale verwenden möchten:

i := If(time.Now().Year() > 2020).Fint(
    func() int { return 3 },
    func() int { return 4 },
)

Abschließende Anmerkung: Wenn Sie Funktionen mit unterschiedlichen Signaturen hätten, könnten Sie sie hier nicht verwenden. In diesem Fall können Sie ein Funktionsliteral mit passender Signatur verwenden, um sie dennoch anwendbar zu machen.

Zum Beispiel, wenn calca() und calcb() auch Parameter hätten (neben dem Rückgabewert):

func calca2(x int) int { return 3 }
func calcb2(x int) int { return 4 }

So könnten Sie sie verwenden:

i := If(time.Now().Year() > 2020).Fint(
    func() int { return calca2(0) },
    func() int { return calcb2(0) },
)

Probieren Sie diese Beispiele auf dem Go Playground aus.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X