it-swarm.com.de

Was ist eine idiomatische Art der Darstellung von Aufzählungen in Go?

Ich versuche, ein vereinfachtes Chromosom darzustellen, das aus N Basen besteht, von denen jede nur einer von {A, C, T, G} sein kann.

Ich möchte die Einschränkungen mit einer Aufzählung formalisieren, aber ich frage mich, was die idiomatischste Art der Emulation einer Aufzählung in Go ist.

460
carbocation

Zitat aus den Sprachspezifikationen: Iota

Innerhalb einer Konstantendeklaration repräsentiert der vordeklarierte Bezeichner iota aufeinanderfolgende untypisierte Ganzzahlkonstanten. Sie wird immer dann auf 0 zurückgesetzt, wenn die reservierte Word-Konstante in der Quelle erscheint und nach jeder ConstSpec erhöht wird. Es kann verwendet werden, um eine Reihe verwandter Konstanten zu erstellen:

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)

In einer ExpressionList ist der Wert jedes Jota derselbe, da er nur nach jeder ConstSpec erhöht wird:

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
)

In diesem letzten Beispiel wird die implizite Wiederholung der letzten nicht leeren Ausdrucksliste ausgenutzt.


So könnte Ihr Code sein

const (
        A = iota
        C
        T
        G
)

oder

type Base int

const (
        A Base = iota
        C
        T
        G
)

wenn Sie möchten, dass die Basen ein von int getrennter Typ sind.

600
zzzz

In Bezug auf die Antwort von jnml könnten Sie neue Instanzen des Basistyps verhindern, indem Sie den Basistyp überhaupt nicht exportieren (d. H. In Kleinbuchstaben schreiben). Bei Bedarf können Sie eine exportierbare Schnittstelle mit einer Methode erstellen, die einen Basistyp zurückgibt. Diese Schnittstelle könnte in Funktionen von außen verwendet werden, die sich mit Basen befassen, d.h.

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
}

Im Hauptpaket ist a.Baser jetzt praktisch wie eine Aufzählung. Nur innerhalb eines Pakets können Sie neue Instanzen definieren.

80
metakeule

Sie können es so machen:

type MessageType int32

const (
    TEXT   MessageType = 0
    BINARY MessageType = 1
)

Mit diesem Code sollte der Compiler die Art der Aufzählung überprüfen

20
Azat

Ab Version 1.4 wurde das Tool go generate zusammen mit dem Befehl stringer eingeführt, mit dem Sie Ihre Enumeration leicht debuggen und ausdrucken können.

17
Moshe Revah

Es ist richtig, dass die obigen Beispiele für die Verwendung von const und iota die idiomatischste Art sind, primitive Enums in Go darzustellen. Aber was ist, wenn Sie nach einer Möglichkeit suchen, eine umfassendere Aufzählung zu erstellen, die dem Typ ähnelt, den Sie in einer anderen Sprache wie Java oder Python sehen würden?

Eine sehr einfache Möglichkeit, ein Objekt zu erstellen, das wie eine String-Aufzählung in Python aussieht und sich anfühlt, wäre:

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

Angenommen, Sie wollten auch einige Dienstprogrammmethoden, wie Colors.List() und Colors.Parse("red"). Und deine Farben waren komplexer und mussten eine Struktur sein. Dann könnten Sie etwas in der Art machen:

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

An diesem Punkt funktioniert es sicher, aber es könnte Ihnen nicht gefallen, wie Sie wiederholt Farben definieren müssen. Wenn Sie dies zu diesem Zeitpunkt beseitigen möchten, können Sie Tags für Ihre Struktur verwenden und ein wenig darüber nachdenken, um sie einzurichten. Hoffentlich reicht dies jedoch aus, um die meisten Benutzer abzudecken.

8
Becca Petrin

Ich bin sicher, wir haben hier viele gute Antworten. Aber ich habe nur darüber nachgedacht, die Art und Weise, wie ich Aufzählungstypen verwendet habe, hinzuzufügen

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

Dies ist bei weitem eine der idiomatischen Möglichkeiten, wie wir Aufzählungstypen erstellen und in Go verwenden können.

Bearbeiten:

Hinzufügen einer anderen Methode zum Verwenden von Konstanten zum Auflisten

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)

}
6
wandermonk

Hier ist ein Beispiel, das sich bei vielen Aufzählungen als nützlich erweisen wird. Es verwendet Strukturen in Golang und stützt sich auf objektorientierte Prinzipien, um sie alle zu einem kleinen Bündel zusammenzufügen. Keiner der zugrunde liegenden Codes wird geändert, wenn eine neue Enumeration hinzugefügt oder gelöscht wird. Der Prozess ist:

  • Definieren Sie eine Aufzählungsstruktur für enumeration items: EnumItem. Es hat einen Integer- und einen String-Typ.
  • Definieren Sie das enumeration als eine Liste von enumeration items: Enum
  • Erstellen Sie Methoden für die Aufzählung. Einige wurden aufgenommen:
    • enum.Name(index int): Gibt den Namen für den angegebenen Index zurück.
    • enum.Index(name string): Gibt den Namen für den angegebenen Index zurück.
    • enum.Last(): Gibt den Index und den Namen der letzten Aufzählung zurück
  • Fügen Sie Ihre Enumerationsdefinitionen hinzu.

Hier ist ein 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"}}}
2
Aaron