Czym jest idiomatyczny sposób przedstawiania enum w Go?
Próbuję przedstawić uproszczony chromosom, który składa się z N baz, z których każda może być tylko jedną z {A, C, T, G}
.
Chciałbym sformalizować ograniczenia za pomocą enum, ale zastanawiam się, jaki jest najbardziej idiomatyczny sposób emulowania enum w Go.
6 answers
Cytowanie ze specyfikacji języka: Iota
W deklaracji stałej, poprzedzający identyfikator iota reprezentuje kolejne nieprzepisane stałe całkowite. Jest resetowany do 0, gdy słowo kluczowe const pojawi się w źródle i zwiększa się po każdym ConstSpec. Można go użyć do skonstruowania zbioru powiązanych stałych:
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)
W obrębie listy wyrażeń wartość każdej ioty jest taka sama, ponieważ jest zwiększana tylko po każdej 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
)
Ten ostatni przykład wykorzystuje Ukryte powtórzenie ostatniej niepustej listy wyrażeń.
Więc Twój kod może być jak
const (
A = iota
C
T
G
)
Lub
type Base int
const (
A Base = iota
C
T
G
)
Jeśli chcesz, aby bazy były oddzielnym typem od int.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-20 17:07:34
Odwołując się do odpowiedzi jnml, możesz zapobiec nowym instancjom typu bazowego, nie eksportując w ogóle typu bazowego (tzn. pisząc go małymi literami). W razie potrzeby można utworzyć interfejs eksportowalny, który ma metodę zwracającą typ bazowy, tak aby ten interfejs mógł być używany w funkcjach z zewnątrz, które zajmują się bazami, np.
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
}
Wewnątrz głównego pakietu a.Baser
jest teraz jak enum.
Tylko wewnątrz pakietu a można zdefiniować nowe instancje.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-02 07:09:22
Od wersji Go 1.4 narzędzie go generate
zostało wprowadzone wraz z stringer
polecenie, które sprawia, że Twoje wyliczenie jest łatwe do debugowania i drukowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-05 12:01:29
You can make it so:
type MessageType int32
const (
TEXT MessageType = 0
BINARY MessageType = 1
)
Za pomocą tego kompilatora kodu należy sprawdzić typ enum
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-21 11:05:08
To prawda, że powyższe przykłady użycia const
i iota
są najbardziej idiomatycznymi sposobami przedstawiania prymitywnych enum w Go. Ale co, jeśli szukasz sposobu na stworzenie bardziej funkcjonalnego enum podobnego do typu, który można zobaczyć w innym języku, takim jak Java lub Python?
Bardzo prostym sposobem na stworzenie obiektu, który zaczyna wyglądać i czuć się jak string enum w Pythonie jest:
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)
}
Załóżmy, że potrzebujesz również metod użytkowych, takich jak Colors.List()
i Colors.Parse("red")
. I twoje kolory były bardziej złożone i musiały być strukturą. Wtedy możesz zrobić coś takiego:
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())
}
W tym momencie, na pewno to działa, ale może Ci się nie spodobać, jak musisz powtarzalnie definiować kolory. Jeśli w tym momencie chcesz to wyeliminować, możesz użyć tagów na swojej strukturze i zrobić fantazyjne refleksje, aby ją skonfigurować, ale mam nadzieję, że to wystarczy, aby pokryć większość ludzi.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-19 22:54:01
Jestem pewien, że mamy tu wiele dobrych odpowiedzi. Ale po prostu pomyślałem o dodaniu sposobu, w jaki użyłem typów wyliczeniowych
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())
}
Jest to zdecydowanie jeden z idiomatycznych sposobów, w jaki możemy tworzyć typy wyliczeniowe i używać ich w Go.
Edit:
Dodanie innego sposobu użycia stałych do wyliczenia
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)
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-30 08:10:14