我试图代表一个简化的染色体,该染色体由N个碱基组成,每个碱基只能是的一个{A, C, T, G}
。
我想用一个枚举来形式化约束,但是我想知道在Go中最惯用的枚举方式是什么。
我试图代表一个简化的染色体,该染色体由N个碱基组成,每个碱基只能是的一个{A, C, T, G}
。
我想用一个枚举来形式化约束,但是我想知道在Go中最惯用的枚举方式是什么。
Answers:
引用语言规范:Iota
在常量声明中,预声明的标识符iota表示连续的无类型整数常量。每当保留字const出现在源中时,它将重置为0,并在每个ConstSpec之后递增。它可以用来构造一组相关的常量:
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)
在ExpressionList中,每个iota的值都是相同的,因为它仅在每个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
)
最后一个示例利用了最后一个非空表达式列表的隐式重复。
所以你的代码可能像
const (
A = iota
C
T
G
)
要么
type Base int
const (
A Base = iota
C
T
G
)
如果您希望基数是与int分开的类型。
Ord(Base)
不限0..3
于其下限数字类型,但具有相同的限制。这是一种语言设计选择,是安全性和性能之间的折衷。每次触摸Base
键入的值时,请考虑“安全”运行时界限检查。还是应该Base
为算术和for ++
和for 定义值的“溢出”行为--
?等等
iota + 1
不从0开始。
参考jnml的答案,您可以通过根本不导出基本类型(即将其写为小写)来防止基本类型的新实例。如果需要,您可以制作一个具有可返回基本类型的方法的可导出接口。该接口可以在外部处理Base的函数中使用,即
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
}
在主程序包内部a.Baser
实际上就像是一个枚举。只有在包中,您才能定义新实例。
base
仅用作方法接收者的情况,您的方法似乎是完美的。如果您的a
程序包要公开一个带有type参数的函数base
,那么它将很危险。确实,用户可以使用文字值42调用它,函数可以接受base
它,因为它可以转换为整数。为了防止这种情况,做出base
一个struct
:type base struct{value:int}
。问题:您不能再将基声明为常量,只能将模块变量声明为常量。但是42绝不会强制转换base
为该类型。
您可以这样:
type MessageType int32
const (
TEXT MessageType = 0
BINARY MessageType = 1
)
使用此代码,编译器应检查枚举的类型
的确,上面使用const
和的示例iota
是表示Go中原始枚举的最惯用的方式。但是,如果您正在寻找一种创建功能更全面的枚举(类似于您在另一种语言(如Java或Python)中看到的类型)的方法,该怎么办?
创建看起来像Python中的字符串枚举的对象的一种非常简单的方法是:
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)
}
假设您还需要一些实用方法,例如Colors.List()
和Colors.Parse("red")
。而且您的颜色更加复杂,需要成为一种结构。然后,您可能会执行以下操作:
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())
}
在这一点上,确保它可以正常工作,但是您可能不喜欢必须重复定义颜色的方式。如果此时您想消除这种情况,可以在结构上使用标签并做一些花哨的反射来设置它,但是希望这足以覆盖大多数人。
从Go 1.4开始,该go generate
工具已与stringer
命令一起引入,该命令使您的枚举易于调试和打印。
我相信我们在这里有很多好的答案。但是,我只是想增加使用枚举类型的方式
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())
}
到目前为止,这是我们可以创建枚举类型并在Go中使用的惯用方法之一。
编辑:
添加使用常量枚举的另一种方法
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)
}
这是一个有许多枚举的示例。它使用Golang中的结构,并利用面向对象的原理将它们整齐地捆绑在一起。添加或删除新枚举时,基础代码都不会更改。该过程是:
enumeration items
以下对象:EnumItem。它具有整数和字符串类型。enumeration
为以下列表enumeration items
:枚举enum.Name(index int)
:返回给定索引的名称。enum.Index(name string)
:返回给定索引的名称。enum.Last()
:返回上一个枚举的索引和名称这是一些代码:
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"}}}