私は単純化された染色体を表現しようとしています。それはN個の塩基で構成され、それぞれが{A, C, T, G}
の1つにしかなれません。
列挙型を使用して制約を形式化したいのですが、列挙型をエミュレートする最も慣用的な方法はGoにあるのでしょうか。
言語仕様からの引用: イオタ
定数宣言内では、事前宣言されている識別子iotaは連続した型なし整数定数を表します。予約語constがソースに表示され、ConstSpecごとに増加するたびに0にリセットされます。これを使用して、関連する一連の定数を作成できます。
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
)
baseをintとは別の型にしたい場合.
Jnmlの答えを参照すると、Base型をまったくエクスポートしないことで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
は事実上enumのようになっています。パッケージの中だけで新しいインスタンスを定義できます。
Go 1.4以降、go generate
ツールは stringer
コマンドとともに導入され、列挙型を簡単にデバッグおよび印刷できるようになりました。
あなたはそれをそうすることができます:
type MessageType int32
const (
TEXT MessageType = 0
BINARY MessageType = 1
)
このコードでコンパイラはenumの型をチェックするべきです
const
とiota
を使用する上記の例は、Goでプリミティブ列挙体を表現する最も慣用的な方法です。しかし、JavaやPythonのような他の言語で見られるタイプに似た、より機能豊富なenumを作成する方法を探しているならどうでしょうか。
Pythonで文字列enumのように見え、感じるようになるオブジェクトを作成するための非常に簡単な方法は次のようになります。
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())
}
その時点では、確かにうまくいきますが、色を繰り返し定義する方法が気に入らない場合があります。この時点であなたがそれを排除したいのなら、あなたはあなたの構造体の上にタグを使ってそれを設定するためにいくつかの空想を反映することができます、しかしうまくいけばこれはほとんどの人をカバーするのに十分です。
ここにはたくさんの良い答えがあると思います。しかし、列挙型の使い方を追加することを考えただけです。
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で使用できる慣用的な方法の1つです。
編集する
列挙に定数を使用する別の方法を追加する
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
のリストとして定義します。Enumenum.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"}}}