ฉันพยายามที่จะเป็นตัวแทนของโครโมโซมง่ายซึ่งประกอบด้วยฐาน N {A, C, T, G}
ซึ่งแต่ละเพียงสามารถเป็นหนึ่งใน
ฉันต้องการทำให้เป็นข้อ จำกัด ด้วย enum อย่างเป็นทางการ แต่ฉันสงสัยว่าการเลียนแบบ enum เป็นวิธีที่งี่เง่าที่สุดใน Go
ฉันพยายามที่จะเป็นตัวแทนของโครโมโซมง่ายซึ่งประกอบด้วยฐาน N {A, C, T, G}
ซึ่งแต่ละเพียงสามารถเป็นหนึ่งใน
ฉันต้องการทำให้เป็นข้อ จำกัด ด้วย enum อย่างเป็นทางการ แต่ฉันสงสัยว่าการเลียนแบบ enum เป็นวิธีที่งี่เง่าที่สุดใน Go
คำตอบ:
การอ้างอิงจากข้อกำหนดภาษา: Iota
ภายในการประกาศค่าคงที่ตัวระบุที่ประกาศไว้ล่วงหน้า iota แสดงค่าคงที่จำนวนเต็มที่ยังไม่ได้พิมพ์ต่อเนื่อง มันจะถูกรีเซ็ตเป็น 0 เมื่อใดก็ตามที่ const คำสงวนปรากฏขึ้นในแหล่งที่มาและเพิ่มขึ้นหลังจากแต่ละ 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
ค่าสำหรับ arithmetics และสำหรับ++
และ--
? อื่น ๆ
iota + 1
เพื่อไม่ให้เริ่มต้นที่ 0
อ้างถึงคำตอบของ jnml คุณสามารถป้องกันอินสแตนซ์ใหม่ของประเภทฐานโดยไม่ส่งออกประเภทฐานเลย (เช่นเขียนเป็นตัวพิมพ์เล็ก) หากจำเป็นคุณสามารถสร้างอินเทอร์เฟซที่ส่งออกได้ซึ่งมีวิธีการคืนค่าประเภทฐาน อินเทอร์เฟซนี้สามารถใช้ในฟังก์ชั่นจากภายนอกที่จัดการกับฐานคือ
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 ในขณะนี้ ภายในแพ็คเกจเท่านั้นที่คุณสามารถกำหนดอินสแตนซ์ใหม่ได้
base
ใช้เป็นตัวรับเมธอดเท่านั้น หากa
แพ็กเกจของคุณต้องแสดงฟังก์ชั่นที่รับพารามิเตอร์เป็นประเภทbase
มันจะเป็นอันตราย แน่นอนผู้ใช้สามารถเรียกมันด้วยค่าตัวอักษร 42 ซึ่งฟังก์ชั่นจะยอมรับได้base
เนื่องจากมันสามารถส่งไปยัง int เพื่อป้องกันการนี้ทำให้: ปัญหา: คุณไม่สามารถประกาศฐานเป็นค่าคงที่อีกต่อไปเพียงตัวแปรโมดูล แต่ 42 จะไม่ถูกส่งไปยังประเภทนั้น base
struct
type base struct{value:int}
base
คุณสามารถทำได้:
type MessageType int32
const (
TEXT MessageType = 0
BINARY MessageType = 1
)
ด้วยคอมไพเลอร์รหัสนี้ควรตรวจสอบประเภทของ enum
มันเป็นความจริงที่ตัวอย่างข้างต้นของการใช้const
และiota
เป็นวิธีที่ใช้สำนวนที่สุดในการเป็นตัวแทนดั้งเดิมใน En Go แต่ถ้าคุณกำลังมองหาวิธีในการสร้าง enum ที่มีคุณลักษณะครบถ้วนคล้ายกับประเภทที่คุณเห็นในภาษาอื่นเช่น Java หรือ Python
วิธีง่ายๆในการสร้างวัตถุที่เริ่มมีรูปลักษณ์และรู้สึกเหมือนสตริง enum ใน 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
คำสั่งที่ทำให้ enum ของคุณสามารถ debuggable และพิมพ์ได้อย่างง่ายดาย
ฉันแน่ใจว่าเรามีคำตอบที่ดีมากมายที่นี่ แต่ฉันแค่คิดว่าจะเพิ่มวิธีที่ฉันใช้ประเภทที่แจกแจง
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
: 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"}}}