Go'daki enumları temsil etmenin deyimsel yolu nedir?


522

Her biri sadece biri olabilir N üslerinden oluşan basitleştirilmiş bir kromozomu temsil etmeye çalışıyorum {A, C, T, G}.

Kısıtlamaları bir enum ile resmileştirmek istiyorum, ama bir enum taklit etmenin en deyimsel yolunun Go'da ne olduğunu merak ediyorum.


4
Go standart paketlerinde sabit olarak temsil edilirler. Bkz. Golang.org/pkg/os/#pkg-constants
Denys Séguret



7
@icza Bu soru 3 yıl önce soruldu. Bu, zaman okunun çalışır durumda olduğu varsayılarak, bunun bir kopyası olamaz.
carbocation

Yanıtlar:


658

Dil özelliklerinden alıntı: Iota

Sabit bir bildirim içinde, önceden tanımlanmış tanımlayıcı iota, ardışık türlenmemiş tamsayı sabitlerini temsil eder. Ayrılmış sözcük const kaynağı her görüntülendiğinde ve her ConstSpec'ten sonra arttığında 0'a sıfırlanır. Bir dizi ilgili sabitleri oluşturmak için kullanılabilir:

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)

Bir ExpressionList içinde, her iota değeri aynıdır çünkü yalnızca her ConstSpec'ten sonra artırılır:

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
)

Bu son örnek, boş olmayan son ifade listesinin örtük tekrarından yararlanır.


Yani kodunuz şöyle olabilir

const (
        A = iota
        C
        T
        G
)

veya

type Base int

const (
        A Base = iota
        C
        T
        G
)

üslerin int'ten ayrı bir tür olmasını istiyorsanız.


16
büyük örnekler (Ben tam olarak iota davranışı hatırlamıyorum - artırıldığında - spec). Şahsen bir numaraya bir tür vermek istiyorum, bu yüzden argüman, alan vb. Olarak kullanıldığında tip kontrol edilebilir
mna

16
Çok ilginç @jnml. Ancak, statik tip kontrolünün gevşek gibi göründüğünden hayal kırıklığına uğradım, örneğin hiçbir şey beni hiç var olmayan 42 no'lu Base'i
17'de Deleplace

4
Go'nun, örneğin Pascal'ın sahip Ord(Base)olduğu gibi sayısal alt aralık türleri kavramı yoktur, bu nedenle bunlarla sınırlı değildir, 0..3ancak altta yatan sayısal türüyle aynı sınırlara sahiptir. Güvenlik ve performans arasında uzlaşma sağlayan bir dil tasarımı seçeneğidir. BaseYazılan bir değere her dokunduğunuzda "güvenli" çalışma süresi bağlı kontrolleri göz önünde bulundurun . Veya nasıl biri 'taşma davranışlarını tanımlamak gerekir Basearitmetiğin ve değeri ++ve --? Vb
zzzz

7
Jnml'de, anlamsal olarak bile olsa, dilde hiçbir şey Base olarak tanımlanan sabitlerin geçerli Base'in tamamını temsil ettiğini söylemez, sadece bu özel consts'ın Base türünde olduğunu söyler. Başka sabitler de başka bir yerde Base olarak tanımlanabilir ve hatta birbirini dışlamaz (örn. Const Z Base = 0 tanımlanabilir ve geçerli olacaktır).
mna

10
iota + 10'dan başlamamak için kullanabilirsiniz .
Marçal Juan

87

Jnml'nin cevabına istinaden, Baz türünü hiç dışa aktarmayarak (örneğin, küçük harf yazarak) yeni Base türünün örneklerini engelleyebilirsiniz. Gerekirse, temel tür döndüren bir yöntemi olan dışa aktarılabilir bir arabirim oluşturabilirsiniz. Bu arayüz dışarıdan üslerle ilgili işlevlerde kullanılabilir, yani

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
}

Ana paketin içinde a.Baserartık etkili bir numaralandırma gibi. Sadece bir paketin içinde yeni örnekler tanımlayabilirsiniz.


10
Metodunuz basesadece metot alıcısı olarak kullanılan durumlarda mükemmel görünüyor . Eğer seninaPaketiniz bir tür parametre alarak bir işlevi ortayabase tehlikeli olur. Aslında, kullanıcı bunu sadece basebir int'e dökülebildiği için fonksiyonun kabul edeceği 42 değişmez değeri ile çağırabilir . Bunu önlemek için yapmak basea struct: type base struct{value:int}. Sorun: Artık üsleri sabit olarak ilan edemezsiniz, sadece modül değişkenleri. Ancak 42 asla basebu türden bir oyuncuya aktarılmaz .
Niriel

6
@metakeule Örneğinizi anlamaya çalışıyorum ama değişken isimlerindeki seçiminiz bunu son derece zorlaştırdı.
anon58192932

1
Bu benim örneklerdeki böceklerimden biri. FGS, cazip olduğunu fark ettim, ancak değişkeni türle aynı şekilde adlandırmayın!
Graham Nicholls

27

Bunu şu şekilde yapabilirsiniz:

type MessageType int32

const (
    TEXT   MessageType = 0
    BINARY MessageType = 1
)

Bu kod ile derleyici enum türünü kontrol etmelidir


5
Sabitler genellikle büyük harfle değil, normal deve harfiyle yazılır. İlk büyük harf, değişkenin dışa aktarıldığı anlamına gelir; bu, istediğiniz gibi olabilir veya olmayabilir.
425nesp

1
Git kaynak kodunda, bazen sabitlerin tümü büyük ve bazen deve olduğu bir karışım olduğunu fark ettim. Bir spesifikasyona referansınız var mı?
Jeremy Gailor

@JeremyGailor 425nesp, normal tercihin geliştiricilerin bunları dışa aktarılmamış sabitler olarak kullanmaları için deve kullanın. Geliştirici, dışa aktarılması gerektiğini belirlerse, yerleşik bir tercih olmadığından tüm büyük harfleri veya büyük harfleri kullanmaktan çekinmeyin. Bkz. Golang Kod İnceleme Önerileri ve Sabitler Üzerine Etkili Git Bölümü
waynethec

Bir tercih var. Değişkenler, fonksiyonlar, türler ve diğerleri gibi, sabit isimler ALLCAPS değil, mixCaps veya MixedCaps olmalıdır. Kaynak: Git Kodu İnceleme Yorumları .
Rodolfo Carvalho

Örneğin, bir MessageType bekleyen işlevlerin türlenmemiş sayısal sabitleri mutlu bir şekilde kabul edeceğini unutmayın, örn. 7. Ayrıca, herhangi bir int32'yi MessageType'a yayınlayabilirsiniz. Eğer bunun farkındaysanız, bunun en deyimsel yol olduğunu düşünüyorum.
Kosta

23

Yukarıdaki örneklerin constve iotaGo'daki ilkel sıralamaları temsil etmenin en deyimsel yolları olduğu doğrudur . Peki, Java veya Python gibi başka bir dilde göreceğiniz türe benzer daha tam özellikli bir numaralandırma oluşturmak için bir yol arıyorsanız?

Python'da dize sıralaması gibi görünmeye ve hissetmeye başlayan bir nesne oluşturmanın çok basit bir yolu:

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)
}

Ayrıca Colors.List(), veColors.Parse("red") . Ve renkleriniz daha karmaşıktı ve bir yapı olması gerekiyordu. O zaman böyle bir şey yapabilirsiniz:

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())
}

Bu noktada, işe yaradığından emin olun, ancak renkleri tekrar tekrar nasıl tanımlamanız gerektiğinden hoşlanmayabilirsiniz. Bu noktada bunu ortadan kaldırmak isterseniz, yapınızdaki etiketleri kullanabilir ve ayarlamak için süslü bir yansıma yapabilirsiniz, ancak umarım bu çoğu insanı kapsamak için yeterlidir.


19

Go 1.4'ten itibaren go generatearaç, stringernumaralandırmanızın kolayca hata ayıklanabilir ve yazdırılabilir olmasını sağlayan komutla birlikte tanıtıldı .


Oposite çözüm olduğunu biliyor musunuz? Dize demek istiyorum -> MyType. Tek yönlü çözüm ideal olmaktan uzak olduğu için. İşte istediğim şeyi yapan sb gist - ancak elle yazmak hata yapmak kolaydır.
SR

11

Eminim burada çok iyi cevaplarımız var. Ancak, numaralandırılmış türleri kullanma şeklimi eklemeyi düşündüm

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())
}

Bu, Numaralandırılmış türler oluşturup Go'da kullanabileceğimiz deyimsel yollardan biridir.

Düzenle:

Numaralandırmak için sabitleri kullanmanın başka bir yolunu ekleme

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)

}

2
Dize değerleriyle sabitleri bildirebilirsiniz. IMO, bunları görüntülemek istiyorsanız ve aslında sayısal değere ihtiyacınız yoksa bunu yapmak daha kolaydır.
cbednarski

4

İşte birçok numaralandırma olduğunda faydalı olacak bir örnek. Golang'daki yapıları kullanır ve hepsini düzgün küçük bir pakette birleştirmek için Nesneye Yönelik İlkeleri kullanır. Yeni bir numaralandırma eklendiğinde veya silindiğinde, temel kodun hiçbiri değişmez. İşlem:

  • Aşağıdakiler için bir numaralandırma yapısı tanımlayın enumeration items: EnumItem . Bir tamsayı ve dize türüne sahiptir.
  • Öğesini aşağıdakilerin enumerationbir listesi olarak tanımlayın enumeration items: Enum
  • Numaralandırma için yöntemler oluşturun. Birkaç tanesi dahil edildi:
    • enum.Name(index int): verilen dizinin adını döndürür.
    • enum.Index(name string): verilen dizinin adını döndürür.
    • enum.Last(): son numaralandırmanın dizinini ve adını döndürür
  • Numaralandırma tanımlarınızı ekleyin.

İşte bazı kod:

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"}}}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.