En C / C ++ (y en muchos idiomas de esa familia), un idioma común para declarar e inicializar una variable dependiendo de una condición utiliza el operador condicional ternario:
int index = val > 0 ? val : -val
Go no tiene el operador condicional. ¿Cuál es la forma más idiomática de implementar el mismo código que el anterior? Llegué a la siguiente solución, pero parece bastante detallada
var index int
if val > 0 {
index = val
} else {
index = -val
}
¿Hay algo mejor?
int index = -val + 2 * val * (val > 0);
Respuestas:
Como se señaló (y es de esperar, como era de esperar), el uso
if+else
es de hecho la forma idiomática de hacer condicionales en Go.Sin
var+if+else
embargo, además del bloque de código completo , esta ortografía también se usa a menudo:y si tiene un bloque de código que es lo suficientemente repetitivo, como el equivalente de
int value = a <= b ? a : b
, puede crear una función para contenerlo:El compilador incorporará funciones tan simples, por lo que es rápido, más claro y más corto.
fuente
c := (map[bool]int{true: a, false: a - 1})[a > b]
es un ejemplo de ofuscación en mi humilde opinión, incluso si funciona.if/else
es el enfoque idiomática entonces tal vez podría Golang considere dejarif/else
cláusulas devuelven un valor:x = if a {1} else {0}
. Ir no sería el único idioma para trabajar de esta manera. Un ejemplo convencional es Scala. Ver: alvinalexander.com/scala/scala-ternary-operator-syntaxNo Go no tiene un operador ternario, el uso de la sintaxis if / else es la forma idiomática.
fuente
if-else
bloque? ¿Y quién dice queif-else
no se abusa de la misma manera? No te estoy atacando, solo siento que la excusa de los diseñadores no es lo suficientemente válidaSuponga que tiene la siguiente expresión ternaria (en C):
El enfoque idiomático en Go sería simplemente usar un
if
bloque:Sin embargo, eso podría no ajustarse a sus requisitos. En mi caso, necesitaba una expresión en línea para una plantilla de generación de código.
Usé una función anónima evaluada inmediatamente:
Esto asegura que ambas ramas no sean evaluadas también.
fuente
expr1 ? expr2 : expr3
. Si seexpr1
evalúa comotrue
,expr2
se evalúa y es el resultado de la expresión. De lo contrario,expr3
se evalúa y se proporciona como resultado. Esto es de la sección 2.11 del lenguaje de programación ANSI C de K&R. La solución My Go conserva estas semánticas específicas. @ Wolf ¿Puedes aclarar lo que estás sugiriendo?El mapa ternario es fácil de leer sin paréntesis:
fuente
simple and clear code is better than creative code.
Esto no superará si / de lo contrario y requiere lanzamiento pero funciona. FYI:
BenchmarkAbsTernary-8 100000000 18.8 ns / op
BenchmarkAbsIfElse-8 2000000000 0.27 ns / op
fuente
Si todas sus ramas producen efectos secundarios o son computacionalmente costosas, lo siguiente sería una refactorización de preservación semántica :
normalmente sin sobrecarga (en línea) y, lo más importante, sin saturar su espacio de nombres con funciones auxiliares que solo se usan una vez (lo que dificulta la legibilidad y el mantenimiento). Ejemplo en vivo
Observe si aplicara ingenuamente el enfoque de Gustavo :
obtendrías un programa con un comportamiento diferente ; ¡en caso de que el
val <= 0
programa imprima un valor no positivo mientras no debería! (De manera análoga, si invierte las ramas, introduciría sobrecarga llamando innecesariamente a una función lenta).fuente
abs
función en el código original (bueno, me gustaría cambiar<=
a<
). En su ejemplo veo una inicialización, que en algunos casos es redundante y podría ser expansiva. ¿Puedes aclarar: explica tu idea un poco más?printPositiveAndReturn
solo se llama para números positivos. Por el contrario, siempre ejecutando una rama, luego "arreglar" el valor con la ejecución de una rama diferente no deshace los efectos secundarios de la primera rama .Prefacio: Sin discutir que ese
if else
es el camino a seguir, aún podemos jugar y encontrar placer en las construcciones habilitadas por el lenguaje.La siguiente
If
construcción está disponible en migithub.com/icza/gox
biblioteca con muchos otros métodos, siendo elbuiltinx.If
tipo.Go permite adjuntar métodos a cualquier tipo definido por el usuario , incluidos los tipos primitivos como
bool
. Podemos crear un tipo personalizado teniendobool
como tipo subyacente , y luego con una conversión de tipo simple en la condición, tenemos acceso a sus métodos. Métodos que reciben y seleccionan de los operandos.Algo como esto:
¿Cómo podemos usarlo?
Por ejemplo, un ternario que hace
max()
:Un ternario haciendo
abs()
:Esto se ve genial, es simple, elegante y eficiente (también es elegible para la inserción ).
Una desventaja en comparación con un operador ternario "real": siempre evalúa todos los operandos.
Para lograr una evaluación diferida y solo si es necesaria, la única opción es usar funciones (ya sea funciones o métodos declarados , o literales de función ), que solo se llaman cuando / si es necesario:
Uso: supongamos que tenemos estas funciones para calcular
a
yb
:Luego:
Por ejemplo, la condición es el año actual> 2020:
Si queremos usar literales de función:
Nota final: si tuviera funciones con diferentes firmas, no podría usarlas aquí. En ese caso, puede usar una función literal con la firma correspondiente para que sigan siendo aplicables.
Por ejemplo, si
calca()
ycalcb()
también tuviera parámetros (además del valor de retorno):Así es como puedes usarlos:
Pruebe estos ejemplos en Go Playground .
fuente
La respuesta de eold es interesante y creativa, tal vez incluso inteligente.
Sin embargo, se recomienda hacer en su lugar:
Sí, ambos se compilan esencialmente en el mismo ensamblado, sin embargo, este código es mucho más legible que llamar a una función anónima solo para devolver un valor que podría haberse escrito en la variable en primer lugar.
Básicamente, un código simple y claro es mejor que el código creativo.
Además, cualquier código que use un literal de mapa no es una buena idea, porque los mapas no son ligeros en absoluto en Go. Desde Go 1.3, el orden de iteración aleatorio para mapas pequeños está garantizado, y para hacer cumplir esto, se ha vuelto un poco menos eficiente en cuanto a memoria para mapas pequeños.
Como resultado, hacer y eliminar numerosos mapas pequeños requiere mucho espacio y tiempo. Tenía un código que usaba un mapa pequeño (es probable que haya dos o tres teclas, pero el caso de uso común era solo una entrada) Pero el código era lento. Estamos hablando de al menos 3 órdenes de magnitud más lento que el mismo código reescrito para usar una clave de división dual [index] => data [index] map. Y probablemente fue más. Como algunas operaciones que antes tardaban un par de minutos en ejecutarse, comenzaron a completarse en milisegundos.
fuente
simple and clear code is better than creative code
- Esto me gusta mucho, pero me estoy confundiendo un poco en la última sección despuésdog slow
, ¿tal vez esto también podría ser confuso para los demás?m := map[string]interface{} { a: 42, b: "stuff" }
, y luego en otra función iterando a través de él:for key, val := range m { code here }
Después de cambiar a un sistema de dos divisiones:,keys = []string{ "a", "b" }, data = []interface{}{ 42, "stuff" }
y luego iterar a través defor i, key := range keys { val := data[i] ; code here }
cosas similares aceleradas 1000 veces.Las frases ingenuas, aunque rechazadas por los creadores, tienen su lugar.
Este resuelve el problema de la evaluación diferida permitiéndole, opcionalmente, pasar funciones para evaluar si es necesario:
Salida
interface{}
para satisfacer la operación de conversión interna.c
.La solución independiente aquí también es buena, pero podría ser menos clara para algunos usos.
fuente