Wat is een idiomatische manier om ENUMS in Go te vertegenwoordigen?

Ik probeer een vereenvoudigd chromosoom te vertegenwoordigen, dat bestaat uit n-basen, die elk alleen een van {A, C, T, G}kunnen zijn.

Ik zou graag de beperkingen met een ENUM formaliseren, maar ik vraag me af wat de meest idiomatische manier om een ​​ENUM te emuleren, in gang is.


Antwoord 1, Autoriteit 100%

citeren uit de taalspecificaties: iota

Binnen een constante verklaring vertegenwoordigt de voorafgekeurde identificator IOTA opeenvolgende niet-getaleerde gehele constanten. Het wordt gereset naar 0 wanneer het gereserveerde woord const verschijnt in de bron en stappen na elke CONSTSPEC. Het kan worden gebruikt om een ​​reeks gerelateerde constanten te construeren:

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)

Binnen een expressionist, is de waarde van elke IOTA hetzelfde omdat het na elke CONSTPEC alleen wordt verhoogd:

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
)

Dit laatste voorbeeld maakt gebruik van de impliciete herhaling van de laatste niet-lege lijst met uitdrukkingen.


Dus je code zou kunnen zijn als

const (
        A = iota
        C
        T
        G
)

of

type Base int
const (
        A Base = iota
        C
        T
        G
)

als je wilt dat basen een apart type zijn van int.


Antwoord 2, autoriteit 13%

Verwijzend naar het antwoord van jnml, zou je nieuwe instanties van het Base-type kunnen voorkomen door het Base-type helemaal niet te exporteren (d.w.z. schrijf het in kleine letters). Indien nodig kunt u een exporteerbare interface maken met een methode die een basistype retourneert. Deze interface kan worden gebruikt in functies van buitenaf die te maken hebben met Bases, d.w.z.

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
}

In het hoofdpakket is a.Basernu in feite een opsomming.
Alleen binnen het a-pakket kunt u nieuwe instanties definiëren.


Antwoord 3, autoriteit 6%

Je kunt het zo maken:

type MessageType int32
const (
    TEXT   MessageType = 0
    BINARY MessageType = 1
)

Met deze code moet compiler het type enum controleren


Antwoord 4, autoriteit 5%

Het is waar dat de bovenstaande voorbeelden van het gebruik van consten iotade meest idiomatische manieren zijn om primitieve opsommingen in Go weer te geven. Maar wat als u op zoek bent naar een manier om een uitgebreidere opsomming te maken, vergelijkbaar met het type dat u in een andere taal zoals Java of Python zou zien?

Een heel eenvoudige manier om een object te maken dat eruitziet en aanvoelt als een string-enum in Python zou zijn:

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

Stel dat u ook enkele hulpprogramma’s wilt, zoals Colors.List()en Colors.Parse("red"). En je kleuren waren complexer en moesten een structuur zijn. Dan zou je iets als dit kunnen doen:

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

Op dat moment werkt het zeker, maar misschien vind je het niet leuk hoe je herhaaldelijk kleuren moet definiëren. Als je dat op dit moment wilt elimineren, kun je tags op je struct gebruiken en wat fantasieën doen om het in te stellen, maar hopelijk is dit genoeg om de meeste mensen te dekken.


Antwoord 5, autoriteit 3%

Vanaf Go 1.4 is de go generate-tool geïntroduceerd samen met de stringer-commando waarmee uw opsomming gemakkelijk kan worden opgespoord en afgedrukt.


Antwoord 6, autoriteit 3%

Er is een manier met struct namespace.

Het voordeel is dat alle enum-variabelen onder een specifieke naamruimte staan om vervuiling te voorkomen.
Het probleem is dat we alleen varkonden gebruiken en niet const

type OrderStatusType string
var OrderStatus = struct {
    APPROVED         OrderStatusType
    APPROVAL_PENDING OrderStatusType
    REJECTED         OrderStatusType
    REVISION_PENDING OrderStatusType
}{
    APPROVED:         "approved",
    APPROVAL_PENDING: "approval pending",
    REJECTED:         "rejected",
    REVISION_PENDING: "revision pending",
}

Antwoord 7, autoriteit 2%

Ik weet zeker dat we hier veel goede antwoorden hebben. Maar ik dacht er gewoon aan om de manier toe te voegen waarop ik opgesomde typen heb gebruikt

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

Dit is verreweg een van de idiomatische manieren waarop we Enumerated-typen kunnen maken en in Go kunnen gebruiken.

Bewerken:

Een andere manier toevoegen om constanten te gebruiken om op te sommen

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

Antwoord 8

Voor een use-case als deze kan het handig zijn om een stringconstante te gebruiken, zodat deze kan worden omgezet in een JSON-string. In het volgende voorbeeld wordt []Base{A,C,G,T}gemarshaleerd naar ["adenine","cytosine","guanine","thymine"].

type Base string
const (
    A Base = "adenine"
    C      = "cytosine"
    G      = "guanine"
    T      = "thymine"
)

Bij gebruik van iotaworden de waarden gegroepeerd in gehele getallen. In het volgende voorbeeld wordt []Base{A,C,G,T}gemarshald naar [0,1,2,3].

type Base int
const (
    A Base = iota
    C
    G
    T
)

Hier is een voorbeeld waarin beide benaderingen worden vergeleken:

https://play.golang.org/p/VvkcWvv-Tvj


Antwoord 9

Hier is een voorbeeld dat handig zal zijn als er veel opsommingen zijn. Het maakt gebruik van structuren in Golang en maakt gebruik van objectgeoriënteerde principes om ze allemaal samen te binden in een nette kleine bundel. Geen van de onderliggende code zal veranderen wanneer een nieuwe opsomming wordt toegevoegd of verwijderd. Het proces is:

  • Definieer een opsommingsstructuur voor enumeration items: EnumItem. Het heeft een geheel getal en een tekenreeks.
  • Definieer de enumerationals een lijst van enumeration items: Enum
  • Bouw methoden voor de opsomming. Er zijn er een aantal opgenomen:
    • enum.Name(index int): retourneert de naam voor de gegeven index.
    • enum.Index(name string): retourneert de naam voor de gegeven index.
    • enum.Last(): retourneert de index en de naam van de laatste opsomming
  • voeg uw opsommingsdefinities toe.

Hier is een 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"}}}

Antwoord 10

Refactured https://stackoverflow.com/a/17989915/863651 Om het een beetje leesbaar te maken:

package SampleEnum
type EFoo int
const (
    A EFoo = iota
    C
    T
    G
)
type IEFoo interface {
    Get() EFoo
}
func(e EFoo) Get() EFoo { // every EFoo must fulfill the IEFoo interface
    return e
}
func(e EFoo) otherMethod()  { // "private"
    //some logic
}

Other episodes