J'essaie de représenter un chromosome simplifié, constitué de N bases, chacune d'entre elles ne pouvant être que l'une des {A, C, T, G}
.
J'aimerais formaliser les contraintes avec une énumération, mais je me demande quelle est la manière la plus idiomatique d'imiter une énumération dans Go.
Citant les spécifications de langue: Iota
Dans une déclaration de constante, l'identifiant prédéfini iota représente des constantes entières non typées successives. Il est réinitialisé à 0 chaque fois que le mot Word réservé apparaît dans la source et s'incrémente après chaque ConstSpec. Il peut être utilisé pour construire un ensemble de constantes liées:
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)
Dans un ExpressionList, la valeur de chaque iota est identique car elle n'est incrémentée qu'après chaque 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
)
Ce dernier exemple exploite la répétition implicite de la dernière liste d'expressions non vides.
Donc, votre code pourrait être comme
const (
A = iota
C
T
G
)
ou
type Base int
const (
A Base = iota
C
T
G
)
si vous voulez que les bases soient un type distinct de int.
En vous référant à la réponse de jnml, vous pouvez empêcher de nouvelles instances de type Base en n’exportant pas du tout le type Base (c’est-à-dire, écrivez-le en minuscule). Si nécessaire, vous pouvez créer une interface exportable avec une méthode qui renvoie un type de base. Cette interface pourrait être utilisée dans des fonctions de l’extérieur qui traitent des bases, c.-à-d.
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
}
À l'intérieur du paquet principal, a.Baser
ressemble effectivement à un enum maintenant. Vous ne pouvez définir de nouvelles instances que dans le package.
À partir de Go 1.4, l'outil go generate
a été introduit avec la commande stringer
qui rend votre enum facile à déboguer et à imprimer.
Vous pouvez le faire ainsi:
type MessageType int32
const (
TEXT MessageType = 0
BINARY MessageType = 1
)
Avec ce code, le compilateur devrait vérifier le type d’énumération
Il est vrai que les exemples ci-dessus d'utilisation de const
et iota
sont les moyens les plus idiomatiques de représenter des énumérations primitives dans Go. Mais que se passe-t-il si vous cherchez un moyen de créer une énumération plus complète similaire à celle que vous verriez dans un autre langage comme Java ou Python?
Un moyen très simple de créer un objet qui commence à ressembler à une énumération de chaîne en Python serait:
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)
}
Supposons que vous souhaitiez également utiliser des méthodes utilitaires, telles que Colors.List()
et Colors.Parse("red")
. Et vos couleurs étaient plus complexes et devaient être une structure. Ensuite, vous pourriez faire quelque chose un peu comme ceci:
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())
}
À ce stade, cela fonctionne, mais vous n’aimerez peut-être pas la définition répétée des couleurs. Si, à ce stade, vous souhaitez éliminer ce problème, vous pouvez utiliser des balises sur votre structure et effectuer des réflexions sophistiquées pour la configurer, mais espérons que cela suffira à couvrir la plupart des gens.
Je suis sûr que nous avons beaucoup de bonnes réponses ici. Mais, je viens de penser à ajouter la façon dont j'ai utilisé les types énumérés
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())
}
C’est de loin l’un des moyens idiomatiques de créer des types énumérés et de les utiliser dans Go.
Modifier:
Ajout d'une autre façon d'utiliser des constantes pour énumérer
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)
}
Voici un exemple qui s’avérera utile lorsqu’il ya plusieurs énumérations. Il utilise des structures à Golang et s’appuie sur des principes orientés objet pour les lier ensemble dans un joli petit paquet. Aucun du code sous-jacent ne changera lorsqu'une nouvelle énumération est ajoutée ou supprimée. Le processus est:
enumeration items
: EnumItem. Il a un type entier et une chaîne.enumeration
comme une liste de enumeration items
: Enumenum.Name(index int)
: renvoie le nom de l'index donné.enum.Index(name string)
: renvoie le nom de l'index donné.enum.Last()
: renvoie l'index et le nom de la dernière énumérationVoici du code:
type EnumItem struct {
index int
name string
}
type Enum struct {
items []EnumItem
}
func (enum Enum) Name(findIndex int) string {
for _, item := range enum.items {
if item.index == findIndex {
return item.name
}
}
return "ID not found"
}
func (enum Enum) Index(findName string) int {
for idx, item := range enum.items {
if findName == item.name {
return idx
}
}
return -1
}
func (enum Enum) Last() (int, string) {
n := len(enum.items)
return n - 1, enum.items[n-1].name
}
var AgentTypes = Enum{[]EnumItem{{0, "StaffMember"}, {1, "Organization"}, {1, "Automated"}}}
var AccountTypes = Enum{[]EnumItem{{0, "Basic"}, {1, "Advanced"}}}
var FlagTypes = Enum{[]EnumItem{{0, "Custom"}, {1, "System"}}}