गो में दुश्मनी का प्रतिनिधित्व करने का एक मुहावरेदार तरीका क्या है?


522

मैं एक सरल गुणसूत्र का प्रतिनिधित्व करने की कोशिश कर रहा हूं, जिसमें एन कुर्सियां ​​हैं, जिनमें से प्रत्येक केवल एक हो सकता है {A, C, T, G}

मैं एक एनम के साथ बाधाओं को औपचारिक करना चाहता हूं, लेकिन मैं सोच रहा हूं कि एनम का अनुकरण करने का सबसे मुहावरेदार तरीका क्या है।


4
जाने के लिए मानक पैकेज में उन्हें स्थिरांक के रूप में दर्शाया जाता है। देखें golang.org/pkg/os/#pkg-constants
Denys Séguret

2
संबं
धत लं क


7
@icza यह सवाल उससे 3 साल पहले पूछा गया था। यह उस एक का डुप्लिकेट नहीं हो सकता है, यह मानते हुए कि समय का तीर कार्य क्रम में है।
कार्बोकेशन

जवाबों:


658

भाषा ऐनक से उद्धृत: Iota

एक निरंतर घोषणा के भीतर, पूर्वनिर्धारित पहचानकर्ता आईओए क्रमिक अप्रकाशित पूर्णांक स्थिरांक का प्रतिनिधित्व करता है। जब भी आरक्षित शब्द कॉन्स्ट्रेक्ट में प्रकट होता है और प्रत्येक कॉन्स्टेप के बाद इंक्रीमेंट में यह 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)

एक्सप्रेशनलिस्ट के भीतर, प्रत्येक आईओटी का मूल्य समान है क्योंकि यह प्रत्येक कॉन्स्टेपेक के बाद केवल बढ़ा हुआ है:

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
)

अगर आप चाहते हैं कि कुर्सियां ​​इंट से अलग प्रकार की हों।


16
महान उदाहरण (मुझे सटीक आईओटी व्यवहार याद नहीं था - जब यह बढ़ा हुआ है - कल्पना से)। व्यक्तिगत रूप से मुझे एक एनम को एक प्रकार देना पसंद है, इसलिए तर्क, क्षेत्र, आदि के रूप में उपयोग किए जाने पर इसे टाइप किया जा सकता है
mna

16
बहुत दिलचस्प @jnml। लेकिन मैं निराश हूं कि स्थिर टाइप-चेकिंग ढीली लगती है, उदाहरण के लिए मुझे आधार n ° 42 का उपयोग करने से कुछ भी नहीं रोकता है जो कभी अस्तित्व में नहीं थी: play.golang.org/p/oH7eiXBxhR
Deleplace

4
गो के पास संख्यात्मक सब प्रकार की कोई अवधारणा नहीं है, जैसे कि पास्कल है, इसलिए Ord(Base)यह सीमित नहीं है, 0..3लेकिन इसके संख्यात्मक प्रकार के समान सीमाएं हैं। यह एक भाषा डिजाइन विकल्प है, सुरक्षा और प्रदर्शन के बीच समझौता। Baseटाइप किए गए मान को छूते समय हर बार "सुरक्षित" रन टाइम चेक पर विचार करें । या कैसे एक परिभाषित करना चाहिए की 'अतिप्रवाह के व्यवहार Basearithmetics के लिए और के लिए मूल्य ++और --? आदि
zzzz

7
Jnml पर पूरक करने के लिए, यहां तक ​​कि शब्दार्थ भी, भाषा में कुछ भी नहीं कहता है कि बेस के रूप में परिभाषित कास्ट्स वैध बेस की पूरी श्रृंखला का प्रतिनिधित्व करते हैं, यह सिर्फ यह कहता है कि ये विशेष प्रकार बेस के हैं। अधिक स्थिरांक को आधार के रूप में भी कहीं और परिभाषित किया जा सकता है, और यह पारस्परिक रूप से अनन्य भी नहीं है (उदाहरण के लिए Z Z = 0 को परिभाषित किया जा सकता है और मान्य होगा)।
mna

10
आप iota + 1
0.32

87

Jnml के उत्तर का जिक्र करते हुए, आप बेस प्रकार के नए इंस्टेंसेस को बेस टाइप को निर्यात न करके (यानी इसे लोअरकेस लिख सकते हैं) रोक सकते हैं। यदि आवश्यक हो, तो आप एक निर्यात करने योग्य इंटरफ़ेस बना सकते हैं जिसमें एक विधि है जो एक आधार प्रकार लौटाता है। इस इंटरफ़ेस का उपयोग बाहर से कार्य करने में किया जा सकता है जो कि Bases के साथ होता है, अर्थात

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प्रभावी रूप से अब एक एनम की तरह है। केवल एक पैकेज के अंदर आप नए उदाहरणों को परिभाषित कर सकते हैं।


10
आपकी विधि उन मामलों के लिए एकदम सही लगती है, जो baseकेवल विधि रिसीवर के रूप में उपयोग किए जाते हैं। यदि आपका aपैकेज प्रकार के पैरामीटर को लेने वाले फ़ंक्शन को उजागर करने के लिए था base, तो यह खतरनाक हो जाएगा। वास्तव में, उपयोगकर्ता इसे केवल शाब्दिक मान 42 के साथ कह सकता है, जिसे फ़ंक्शन स्वीकार करेगा baseक्योंकि इसे एक इंट में डाला जा सकता है। इसे रोकने के लिए : basea । समस्या: आप बेस को अब स्थिरांक के रूप में घोषित नहीं कर सकते, केवल मॉड्यूल चर। लेकिन 42 को कभी भी उस प्रकार का नहीं बनाया जाएगा । structtype base struct{value:int}base
Niriel

6
@metakeule मैं आपके उदाहरण को समझने की कोशिश कर रहा हूं लेकिन चर नामों में आपकी पसंद ने इसे अत्यधिक कठिन बना दिया है।
Anon58192932

1
यह उदाहरणों में मेरे बगबियर्स में से एक है। FGS, मुझे एहसास हुआ कि यह आकर्षक है, लेकिन चर का नाम उसी प्रकार नहीं है!
ग्राहम निकोलस

26

आप इसे बना सकते हैं:

type MessageType int32

const (
    TEXT   MessageType = 0
    BINARY MessageType = 1
)

इस कोड के साथ संकलक को एनम के प्रकार की जांच करनी चाहिए


5
कॉन्स्टेंट आमतौर पर सामान्य ऊंट में लिखे जाते हैं, सभी बड़े अक्षरों में नहीं। प्रारंभिक अपरकेस पत्र का मतलब है कि चर का निर्यात किया जाता है, जो आप चाहते हैं या नहीं हो सकता है।
425nesp

1
मैंने देखा कि गो स्रोत कोड में एक मिश्रण है जहां कभी-कभी स्थिरांक सभी बड़े होते हैं और कभी-कभी वे ऊंट होते हैं। क्या आपके पास किसी युक्ति का संदर्भ है?
जेरेमी गेलर

@ जेरेमीगैलर मुझे लगता है कि 425nesp सिर्फ यह ध्यान दे रहा है कि डेवलपर्स के लिए सामान्य प्राथमिकता है कि वे उन्हें अनएक्सपोर्टेड कॉन्स्टेंट के रूप में उपयोग करें ताकि ऊंट का उपयोग करें । यदि डेवलपर निर्धारित करता है कि इसे निर्यात किया जाना चाहिए, तो सभी अपरकेस या पूंजी मामले का उपयोग करने के लिए स्वतंत्र महसूस करें क्योंकि कोई भी स्थापित प्राथमिकता नहीं है। गोलंग कोड की समीक्षा की सिफारिशें देखें और प्रभावी रूप से स्थिरांक पर जाएं
waynethec

वरीयता है। बस चरों, कार्यों, प्रकारों और अन्य की तरह, निरंतर नामों को मिलाया जाना चाहिए या मिश्रित, ALLCAPS नहीं। स्रोत: जाओ संहिता समीक्षा टिप्पणियाँ
रोडोल्फो कारवाल्हो

22

यह सच है कि उपरोक्त उदाहरणों का उपयोग करना constऔरiota गो में आदिम एनमों का प्रतिनिधित्व करने का सबसे मुहावरेदार तरीका है। लेकिन क्या होगा यदि आप जावा या पायथन जैसी किसी अन्य भाषा में देखे गए प्रकार के समान अधिक पूर्ण रूप से चित्रित किए गए एनम बनाने का मार्ग खोज रहे हैं?

एक बहुत ही सरल तरीके से एक ऐसी वस्तु तैयार की जाती है जो पायथन में स्ट्रिंग एनम की तरह दिखना और महसूस करना शुरू करती है:

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

उस बिंदु पर, सुनिश्चित करें कि यह काम करता है, लेकिन आपको यह पसंद नहीं है कि आपको रंगों को कैसे दोहराना है। यदि इस बिंदु पर आप इसे समाप्त करना चाहते हैं, तो आप अपनी संरचना पर टैग का उपयोग कर सकते हैं और इसे स्थापित करने के लिए कुछ फैंसी प्रतिबिंबित कर सकते हैं, लेकिन उम्मीद है कि यह अधिकांश लोगों को कवर करने के लिए पर्याप्त है।


19

गो 1.4 के रूप में, go generateटूल को एक साथ stringerकमांड के साथ पेश किया गया है जो आपके एनम को आसानी से डिबग और प्रिंट करने योग्य बनाता है।


क्या आपको पता है कि ऑपोजिट घोल है। मेरा मतलब है स्ट्रिंग -> MyType। चूंकि एक तरह से समाधान आदर्श से बहुत दूर है। यहाँ sb gist है जो मैं चाहता हूँ - लेकिन गलती से हाथ से लिखना आसान है।
एसआर

11

मुझे यकीन है कि हमारे यहां बहुत अच्छे जवाब हैं। लेकिन, मैंने सिर्फ उन तरीकों को जोड़ने के बारे में सोचा है जो मैंने enumerated type का उपयोग किया है

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

यह अब तक एक मुहावरेदार तरीका है जिससे हम Enumerated प्रकार बना सकते हैं और 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)

}

2
आप स्ट्रिंग मानों के साथ स्थिरांक घोषित कर सकते हैं। IMO यह करना आसान है कि यदि आप उन्हें प्रदर्शित करने का इरादा रखते हैं और वास्तव में संख्यात्मक मूल्य की आवश्यकता नहीं है।
cbednarski

4

यहां एक उदाहरण दिया गया है जो कई गणनाओं के लिए उपयोगी साबित होगा। यह गोलंग में संरचनाओं का उपयोग करता है, और ऑब्जेक्ट ओरिएंटेड सिद्धांतों को एक साफ छोटे बंडल में एक साथ बांधने के लिए आकर्षित करता है। जब कोई नई गणना जोड़ी जाती है या हटा दी जाती है, तो कोई भी अंतर्निहित कोड नहीं बदलेगा। प्रक्रिया है:

  • के लिए एक गणना संरचना को परिभाषित करें enumeration items: EnumItem । यह एक पूर्णांक और स्ट्रिंग प्रकार है।
  • की enumerationएक सूची के रूप में परिभाषित करें enumeration items: Enum
  • गणना के लिए तरीके बनाएँ। कुछ को शामिल किया गया है:
    • 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"}}}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.