¿Cómo hacer una declaración if else?

104

¿Puedo escribir una declaración simple if-else con asignación de variable en go (golang) como lo haría en php? Por ejemplo:

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

Actualmente tengo que usar lo siguiente:

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

Lo siento, no recuerdo el nombre de esta declaración de control y no pude encontrar la información en el sitio o mediante la búsqueda de Google. : /

Thoroc
fuente
4
Se llama operador ternario ... y no, Go no tiene uno.
Simon Whitehead
6
Creo que la palabra que estás buscando es "ternario"
BenjaminRH
10
Solo para aclarar, un operador ternario es cualquier operador de arity 3, es decir, cualquier operador que une 3 subexpresiones. C resulta que solo tiene uno de esos operadores. Por eso se suele llamar operador ternario. Sin embargo, su nombre real es "operador condicional".
jueves

Respuestas:

131

Como se mencionó en los comentarios, Go no es compatible con los one liners ternarios. La forma más corta en la que puedo pensar es esta:

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

Pero no hagas eso, no vale la pena y solo confundirá a las personas que lean tu código.

Not_a_Golfer
fuente
83
@thoroc Probablemente evitaría eso en la vida real, ya que en mi humilde opinión no es intuitivo, no vale la pena guardar 2 líneas para una menor legibilidad.
Not_a_Golfer
14
@thoroc, por favor escuche lo que dijo Not_a_Golfer. Debe esforzarse por escribir software que se pueda mantener, no lucirse. Los trucos ingeniosos son geniales, pero el próximo que lea tu código (incluyéndote a ti en un par de meses / años) no los apreciará.
kostix
3
Depende, en cuanto a legibilidad. Creo que esto es más legible, pero como cualquier otra cosa, se puede abusar de él. Si necesita declarar una variable que no es necesaria fuera del bloque if, entonces creo que esto es un ganador.
Arjabbar
@Not_a_Golfer no es solo legibilidad por simetría, el contraste estructural con la intención también está en el código compilado, a veces incluso con un costo adicional por una inicialización redundante.
Wolf
5
Oh, alegría, mira cuánto ganó Go al no tener un operador ternario. Aparte de los comentarios de legibilidad realizados, ha cambiado una construcción evaluada de forma perezosa en la que solo se ejecuta una rama a una en la que la primera siempre se ejecuta y la segunda puede ejecutarse además.
itsbruce
29

A menudo uso lo siguiente:

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

básicamente lo mismo que @ Not_a_Golfer's pero usando inferencia de tipos .

xoyuz
fuente
4
Con el mismo defecto: se complica la comprensión cuando se utiliza una solución asimétrica para un requisito obviamente simétrico.
Wolf
2
Y el mismo defecto de convertir lo que debería ser un constructo evaluado de manera perezosa donde solo una de las dos ramas se usará en una donde siempre se evaluará una, a veces ambas (en cuyo caso la primera evaluación fue redundante)
itsbruce
1
Dependiendo del caso de uso, esto aún podría ser muy útil. Por ejemplo, la listeningPath := "production.some.com"; if DEBUG { listeningPath := "development.some.com" }misma velocidad que el ternario para la producción y, en mi humilde opinión, legibilidad bastante buena.
Levita
Normalmente lloro cuando desperdicio intencionalmente el ciclo de la CPU.
alessiosavi
28

Como los demás mencionaron, Gono es compatible con frases ternarias. Sin embargo, escribí una función de utilidad que podría ayudarlo a lograr lo que desea.

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

Aquí hay algunos casos de prueba para mostrar cómo puede 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)
}

Por diversión, escribí funciones de utilidad más útiles como:

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 desea utilizar alguno de estos, puede encontrarlos aquí https://github.com/shomali11/util

Raed Shomali
fuente
12

Gracias por señalar la respuesta correcta.

Acabo de consultar las preguntas frecuentes de Golang (duh) y dice claramente que esto no está disponible en el idioma:

Go tiene el operador?:?

No hay forma ternaria en Go. Puede utilizar lo siguiente para lograr el mismo resultado:

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

información adicional encontrada que podría ser de interés sobre el tema:

Thoroc
fuente
también es posible en una línea var c int; if a > b { c = a } else { c = b }:? Pero sugiero mantenerlo en 5 líneas para formar un bloque de luz para la recreación del lector;)
Wolf
7

Una posible manera de hacer esto en una sola línea, utilizando un mapa, sencilla estoy comprobando si a > bsi es truevoy a asignar ca aotra cosab

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

Sin embargo, esto se ve increíble, pero en algunos casos puede que NO sea la solución perfecta debido al orden de evaluación. Por ejemplo, si estoy comprobando si un objeto no nilobtiene alguna propiedad de él, mire el siguiente fragmento de código que, panicen caso 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}

Debido a que el mapa se creará y construirá primero antes de evaluar la condición, en caso de myObj = nilesto, simplemente entrará en pánico.

Sin olvidar mencionar que aún puede hacer las condiciones en una sola línea, verifique lo siguiente:

var c int
...
if a > b { c = a } else { c = b}
Muhammad Soliman
fuente
4

Use la función lambda en lugar del operador ternario

Ejemplo 1

para dar el máximo int

package main

func main() {

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

Ejemplo 2

Suponga que tiene esta must(err error)función para manejar errores y desea usarla cuando no se cumple una condición. (disfrútelo en 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)
}
Alice Vixie
fuente
2
enfoque interesante, podría ser útil en casos más complejos. :)
thoroc
1

A veces, trato de usar una función anónima para lograr que la definición y la asignación ocurran en la misma línea. como abajo:

a, b = 4, 8

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

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

Ron
fuente
0

Una construcción muy similar está disponible en el idioma

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

*

es decir

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

Puedes usar un cierre para esto:

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

La única queja que tengo con la sintaxis de cierre en Go es que no hay un alias para la función de retorno cero del parámetro cero predeterminado, entonces sería mucho mejor (piense en cómo declara los literales de mapa, matriz y corte con solo un nombre de tipo).

O incluso la versión más corta, como acaba de sugerir un comentarista:

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

Aún necesitaría usar un cierre si necesita dar parámetros a las funciones. Esto podría obviarse en el caso de pasar métodos en lugar de solo funciones, creo, donde los parámetros son la estructura asociada con los métodos.

Louki Sumirniy
fuente
Versión más cortadoif(condition, dothis, dothat)
vellotis
1
Sí, eso sería aún más corto, pasando solo las funciones. Uno solo tiene que tener esta función una vez en una biblioteca de utilidades, y puede usarla a través de su código.
Louki Sumirniy