wake-up-neo.com

Was ist das idiomatische Go-Äquivalent des ternären Operators von C?

In C/C++ (und vielen Sprachen dieser Familie) wird für die Deklaration und Initialisierung einer Variablen in Abhängigkeit von einer Bedingung der folgende ternäre Bedingungsoperator verwendet:

int index = val > 0 ? val : -val

Go hat keinen bedingten Operator. Was ist der idiomatischste Weg, um den gleichen Code wie oben zu implementieren? Ich bin zu der folgenden Lösung gekommen, aber sie scheint ziemlich ausführlich zu sein

var index int

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

Gibt es was besseres

234
Fabien

Wie bereits erwähnt (und hoffentlich nicht überraschend), wird if+else ist in der Tat der idiomatische Weg , um Bedingungen in Go zu erfüllen.

Zusätzlich zu den ausgewachsenen var+if+else Codeblock, diese Schreibweise wird jedoch auch häufig verwendet:

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

und wenn Sie einen Codeblock haben, der sich genug wiederholt, wie das Äquivalent von int value = a <= b ? a : b, können Sie eine Funktion erstellen, um es zu halten:

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

...

value := min(a, b)

Der Compiler integriert so einfache Funktionen, dass sie schneller, klarer und kürzer sind.

195

No Go hat keinen ternären Operator. Die Verwendung der if/else-Syntax ist der idiomatische Weg: http://golang.org/ doc/faq # Does_Go_have_a_ternary_form

58
ishaaq

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

int a = test ? 1 : 2;

Der idiomatische Ansatz in Go wäre, einfach einen if -Block zu verwenden:

var a int

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

Dies entspricht jedoch möglicherweise nicht Ihren Anforderungen. In meinem Fall benötigte ich einen Inline-Ausdruck für eine Codegenerierungsvorlage.

Ich habe eine sofort bewertete anonyme Funktion verwendet:

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

Dies stellt sicher, dass beide Zweige nicht ebenfalls ausgewertet werden.

48
Peter Boyer

Der Kartenternär ist ohne Klammern leicht zu lesen:

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

Dies wird nicht übertreffen, wenn/else und benötigt Besetzung, aber funktioniert. Zu Ihrer Information:

BenchmarkAbsTernary-8 100000000 18,8 ns/op

BenchmarkAbsIfElse-8 2000000000 0,27 ns/op

7
Phillip Dominy

Wenn alle Ihre Zweige Nebenwirkungen haben oder sind rechenintensiv wäre folgendes ein semantisch erhaltenes Refactoring:

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

normalerweise ohne Overhead (inline) und vor allem ohne überladenen Namespace mit Hilfsfunktionen, die nur einmal verwendet werden (was die Lesbarkeit und Wartung beeinträchtigt) . Live-Beispiel

Beachten Sie, wenn Sie sich naiv bewerben würden Gustavos Ansatz :

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

sie würden ein Programm mit einem anderen Verhalten erhalten ; im Fall val <= 0 Programm würde einen nicht positiven Wert ausgeben, während es nicht sollte! (Wenn Sie die Verzweigungen umkehren würden, würden Sie Overhead einführen, indem Sie unnötigerweise eine langsame Funktion aufrufen.)

5
eold

die Antwort von eold ist interessant und kreativ, vielleicht sogar clever.

Es wird jedoch empfohlen, stattdessen Folgendes zu tun:

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

Ja, beide werden im Wesentlichen auf dieselbe Assembly kompiliert. Dieser Code ist jedoch viel besser lesbar als der Aufruf einer anonymen Funktion, um nur einen Wert zurückzugeben, der ursprünglich in die Variable geschrieben worden sein könnte.

Grundsätzlich ist einfacher und klarer Code besser als kreativer Code.

Außerdem ist jeder Code, der ein Kartenliteral verwendet, keine gute Idee, da Karten in Go überhaupt nicht leichtgewichtig sind. Seit Go 1.3 ist die zufällige Iterationsreihenfolge für kleine Karten garantiert, und um dies zu erzwingen, ist die Speichereffizienz für kleine Karten um einiges geringer.

Das Erstellen und Entfernen zahlreicher kleiner Karten ist daher sowohl platz- als auch zeitaufwändig. Ich hatte einen Code, der eine kleine Karte verwendete (zwei oder drei Schlüssel sind wahrscheinlich, aber der allgemeine Anwendungsfall war nur ein Eintrag). Aber der Code war hundeschwach. Es handelt sich um mindestens 3 Größenordnungen langsamer als der Code, der für die Verwendung einer Dual-Slice-Key-Map [index] => data [index] umgeschrieben wurde. Und wahrscheinlich war mehr. Da einige Vorgänge, deren Ausführung zuvor einige Minuten in Anspruch genommen hatte, in Millisekunden abgeschlossen wurden. \

3
Cassy Foesch