¿Qué es una forma idiomática de representar enum en Go?


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.

Author: Philip Kirkbride, 2013-01-20

6 answers

Citando las especificaciones del idioma: Iota

Dentro de una declaración constante, el identificador predeclared iota representa constantes enteras sucesivas sin tipo. Se restablece a 0 cada vez que la palabra reservada const aparece en la fuente y se incrementa 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ía.


Así que su código podría ser como

const (
        A = iota
        C
        T
        G
)

O

type Base int

const (
        A Base = iota
        C
        T
        G
)

Si desea que bases sea un tipo separado de int.

 509
Author: zzzz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-20 17:07:34

Refiriéndose a la respuesta de jnml, podría evitar nuevas instancias de tipo Base al no exportar el tipo Base en absoluto (es decir, escribirlo en minúsculas). Si es necesario, puede hacer una interfaz exportable que tenga un método que devuelva un tipo base, de modo que esta interfaz pueda usarse en funciones del exterior que traten con Bases, es decir,

package a

type base int

const (
    A base = iota
    C
    T
    G
)


type Baser interface {
    Base() base
}

// every base must fullfill 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 a puede definir nuevas instancias.

 63
Author: metakeule,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-02 07:09:22

A partir de Go 1.4, la herramienta go generate se ha introducido junto con el stringer comando que hace que su enumeración sea fácilmente depurable e imprimible.

 15
Author: Moshe Revah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-05 12:01:29

Puedes hacerlo así:

type MessageType int32

const (
    TEXT   MessageType = 0
    BINARY MessageType = 1
)

Con este compilador de código debe comprobar el tipo de enumeración

 10
Author: Azat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-21 11:05:08

Es cierto que los ejemplos anteriores de usar const y iota son las formas más idiomáticas de representar enum primitivos en Go. Pero, ¿qué pasa si estás buscando una manera de crear una enumeración más completa similar al tipo que verías en otro lenguaje como Java o Python?

Una forma muy sencilla de crear un objeto que comience 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 usted podría hacer algo un poco 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 punto, seguro que funciona, pero puede que no te guste cómo tienes que definir colores repetitivamente. Si en este punto desea eliminar eso, podría usar etiquetas en su estructura y hacer un poco de reflexión elegante para configurarlo, pero espero que esto sea suficiente para cubrir a la mayoría de las personas.

 4
Author: Becca Petrin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-19 22:54:01

Estoy seguro de que tenemos muchas buenas respuestas aquí. Pero, solo pensé en agregar la forma en que he utilizado 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, con mucho, una de las formas idiomáticas que podríamos crear tipos Enumerados y usar en Go.

Editar:

Añadiendo 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)

}
 2
Author: wandermonk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-30 08:10:14