私は、N塩基からなる簡略化された染色体を表現しようとしています。N塩基はそれぞれの1つだけです{A, C, T, G}
。
列挙型を使用して制約を形式化したいのですが、列挙型をエミュレートする最も慣用的な方法はGoでどのようになっているのでしょうか。
私は、N塩基からなる簡略化された染色体を表現しようとしています。N塩基はそれぞれの1つだけです{A, C, T, G}
。
列挙型を使用して制約を形式化したいのですが、列挙型をエミュレートする最も慣用的な方法はGoでどのようになっているのでしょうか。
回答:
言語仕様から引用:イオタ
定数宣言内では、事前宣言された識別子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タイプをまったくエクスポートしない(つまり、小文字で書き込む)ことにより、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
は実質的に列挙型のようになっています。パッケージ内でのみ、新しいインスタンスを定義できます。
base
、メソッドのレシーバーとしてのみ使用される場合に最適です。a
パッケージがtypeのパラメーターを取る関数を公開する場合、base
それは危険になります。実際、ユーザーはそれをリテラル値42で呼び出すことができます。これはbase
、intにキャストできるため、関数はこれを受け入れます。これを防ぐにはbase
、struct
:を作成しますtype base struct{value:int}
。問題:基底を定数として宣言することはできなくなり、モジュール変数のみになります。ただし、42がbase
そのタイプのにキャストされることはありません。
あなたはそうすることができます:
type MessageType int32
const (
TEXT MessageType = 0
BINARY MessageType = 1
)
このコードを使用すると、コンパイラは列挙型をチェックする必要があります
これは、使用しての上記の例というのは本当だconst
とiota
ゴーで原始的列挙型を表現する最も慣用的な方法です。しかし、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())
}
その時点で確実に機能しますが、色を繰り返し定義する方法が気に入らない場合があります。この時点でそれを排除したい場合は、構造体でタグを使用し、いくつかの凝ったリフレクションを行って設定できますが、これでほとんどの人をカバーできます。
ここには多くの良い答えがあると思います。しかし、私は列挙型の使用方法を追加することを考えました
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
:列挙型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"}}}