enums - recorrer - que es un enum en java
¿Cuál es una forma idiomática de representar enumeraciones en Go? (6)
Estoy tratando de representar un cromosoma simplificado, que consiste en N bases, cada una de las cuales solo puede ser una de {A, C, T, G}
.
Me gustaría formalizar las restricciones con una enumeración, pero me pregunto cuál es la forma más idiomática de emular una enumeración en Go.
A partir de Go 1.4, la herramienta de go generate
se ha introducido junto con el comando de cadena que hace que su enumeración sea fácilmente depurable e imprimible.
Cotización de las especificaciones de idioma: Iota
Dentro de una declaración constante, el identificador predeclarado iota representa constantes enteras no tipificadas sucesivas. Se restablece a 0 siempre que la palabra reservada const aparezca en la fuente y se incremente después de cada ConstSpec. Se puede usar para construir un conjunto de constantes relacionadas:
const ( // iota is reset to 0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const (
a = 1 << iota // a == 1 (iota has been reset)
b = 1 << iota // b == 2
c = 1 << iota // c == 4
)
const (
u = iota * 42 // u == 0 (untyped integer constant)
v float64 = iota * 42 // v == 42.0 (float64 constant)
w = iota * 42 // w == 84 (untyped integer constant)
)
const x = iota // x == 0 (iota has been reset)
const y = iota // y == 0 (iota has been reset)
Dentro de una ExpressionList, el valor de cada iota es el mismo porque solo se incrementa después de cada ConstSpec:
const (
bit0, mask0 = 1 << iota, 1<<iota - 1 // bit0 == 1, mask0 == 0
bit1, mask1 // bit1 == 2, mask1 == 1
_, _ // skips iota == 2
bit3, mask3 // bit3 == 8, mask3 == 7
)
Este último ejemplo explota la repetición implícita de la última lista de expresiones no vacías.
Entonces tu código podría ser como
const (
A = iota
C
T
G
)
o
type Base int
const (
A Base = iota
C
T
G
)
Si quieres que las bases sean un tipo separado de int.
Es cierto que los ejemplos anteriores de uso de const
y iota
son las formas más idiomáticas de representar enumeraciones primitivas en Go. Pero, ¿qué sucede si está buscando una forma de crear una enumeración más completa similar a la que vería en otro idioma como Java o Python?
Una forma muy sencilla de crear un objeto que comienza a verse y sentirse como una enumeración de cadena en Python sería:
package main
import (
"fmt"
)
var Colors = newColorRegistry()
func newColorRegistry() *colorRegistry {
return &colorRegistry{
Red: "red",
Green: "green",
Blue: "blue",
}
}
type colorRegistry struct {
Red string
Green string
Blue string
}
func main() {
fmt.Println(Colors.Red)
}
Supongamos que también desea algunos métodos de utilidad, como Colors.List()
y Colors.Parse("red")
. Y tus colores eran más complejos y necesitaban ser una estructura. Entonces podrías hacer algo como esto:
package main
import (
"errors"
"fmt"
)
var Colors = newColorRegistry()
type Color struct {
StringRepresentation string
Hex string
}
func (c *Color) String() string {
return c.StringRepresentation
}
func newColorRegistry() *colorRegistry {
red := &Color{"red", "F00"}
green := &Color{"green", "0F0"}
blue := &Color{"blue", "00F"}
return &colorRegistry{
Red: red,
Green: green,
Blue: blue,
colors: []*Color{red, green, blue},
}
}
type colorRegistry struct {
Red *Color
Green *Color
Blue *Color
colors []*Color
}
func (c *colorRegistry) List() []*Color {
return c.colors
}
func (c *colorRegistry) Parse(s string) (*Color, error) {
for _, color := range c.List() {
if color.String() == s {
return color, nil
}
}
return nil, errors.New("couldn''t find it")
}
func main() {
fmt.Printf("%s/n", Colors.List())
}
En ese momento, seguro que funciona, pero es posible que no te guste cómo debes definir los colores repetitivamente. Si en este punto desea eliminar eso, puede usar etiquetas en su estructura y hacer un poco de reflexión para configurarlo, pero espero que esto sea suficiente para cubrir a la mayoría de las personas.
Estoy seguro de que tenemos muchas buenas respuestas aquí. Pero, solo pensé en agregar la forma en que he usado los tipos enumerados
package main
import "fmt"
type Enum interface {
name() string
ordinal() int
values() *[]string
}
type GenderType uint
const (
MALE = iota
FEMALE
)
var genderTypeStrings = []string{
"MALE",
"FEMALE",
}
func (gt GenderType) name() string {
return genderTypeStrings[gt]
}
func (gt GenderType) ordinal() int {
return int(gt)
}
func (gt GenderType) values() *[]string {
return &genderTypeStrings
}
func main() {
var ds GenderType = MALE
fmt.Printf("The Gender is %s/n", ds.name())
}
Esta es, de lejos, una de las formas idiomáticas en que podríamos crear tipos enumerados y usarlos en Go.
Editar:
Agregando otra forma de usar constantes para enumerar
package main
import (
"fmt"
)
const (
// UNSPECIFIED logs nothing
UNSPECIFIED Level = iota // 0 :
// TRACE logs everything
TRACE // 1
// INFO logs Info, Warnings and Errors
INFO // 2
// WARNING logs Warning and Errors
WARNING // 3
// ERROR just logs Errors
ERROR // 4
)
// Level holds the log level.
type Level int
func SetLogLevel(level Level) {
switch level {
case TRACE:
fmt.Println("trace")
return
case INFO:
fmt.Println("info")
return
case WARNING:
fmt.Println("warning")
return
case ERROR:
fmt.Println("error")
return
default:
fmt.Println("default")
return
}
}
func main() {
SetLogLevel(INFO)
}
Puedes hacerlo así:
type MessageType int32
const (
TEXT MessageType = 0
BINARY MessageType = 1
)
Con este código el compilador debería comprobar el tipo de enumeración.
Refiriéndose a la respuesta de jnml, podría evitar nuevas instancias del tipo Base al no exportar el tipo Base en absoluto (es decir, escribirlo en minúscula). Si es necesario, puede crear una interfaz exportable que tenga un método que devuelva un tipo base. Esta interfaz podría usarse en funciones externas que se ocupan de Bases, es decir,
package a
type base int
const (
A base = iota
C
T
G
)
type Baser interface {
Base() base
}
// every base must fulfill the Baser interface
func(b base) Base() base {
return b
}
func(b base) OtherMethod() {
}
package main
import "a"
// func from the outside that handles a.base via a.Baser
// since a.base is not exported, only exported bases that are created within package a may be used, like a.A, a.C, a.T. and a.G
func HandleBasers(b a.Baser) {
base := b.Base()
base.OtherMethod()
}
// func from the outside that returns a.A or a.C, depending of condition
func AorC(condition bool) a.Baser {
if condition {
return a.A
}
return a.C
}
Dentro del paquete principal a.Baser
es efectivamente como una enumeración ahora. Solo dentro del paquete puede definir nuevas instancias.