प्रविष्टि
प्रक्रियात्मक प्रोग्रामिंग भाषाओं की सबसे आम विशेषताओं में से एक सरणी की अवधारणा है। ऐरे सरल लग सकते हैं, लेकिन दूसरी ओर, उन्हें भाषा में जोड़ने से पहले, कई मुद्दों को संबोधित करने की आवश्यकता है, जैसे:
- निश्चित या परिवर्तनशील आकार?
- आकार का प्रकार है?
- बहुआयामी सरणियाँ क्या होंगी?
- एक खाली सरणी की अवधारणा क्या है?
इन प्रश्नों के उत्तर सरणियों को भाषा की एक साधारण विशेषता के रूप में, या इसके डिजाइन के मुख्य भाग के रूप में परिभाषित करेंगे।
गो के शुरुआती दिनों में, इन प्रश्नों के उत्तर खोजने के लिए चर्चा के वर्षों की आवश्यकता थी, इससे पहले कि उनकी अवधारणा ठीक दिखे जैसा हमने सोचा था। एक महत्वपूर्ण कदम
स्लाइस की अवधारणा को बनाना था, जो एक लचीली और एक्स्टेंसिबल डेटा संरचना बनाने के लिए, निश्चित आकार के सरणियों पर आधारित हैं। हालांकि, गोइंग के कई नवागंतुकों ने स्लाइसिंग के सिद्धांतों पर ठोकर खाई, शायद इसलिए कि अन्य भाषाओं के साथ उनके अनुभव ने अपनी छाप छोड़ी है।
इस प्रकाशन में हम इन सभी गलतफहमियों को दूर करने की कोशिश करेंगे। हम इसे टुकड़ों में यह बताएंगे कि
एपेंड फ़ंक्शन कैसे काम करता है, और यह क्यों काम करता है।
सरणियों
Arrays गो भाषा का एक महत्वपूर्ण टुकड़ा है, लेकिन, इमारत की नींव की तरह, यह अधिक दृश्य भागों के नीचे छिपा हुआ है। अधिक दिलचस्प, शक्तिशाली और ध्यान देने योग्य स्लाइसिंग विशेषताओं पर जाने से पहले हमें आपको गति प्राप्त करनी चाहिए।
Arrays अक्सर गो कार्यक्रमों में नहीं पाए जाते हैं, क्योंकि एक सरणी के प्रकार में इसका आकार शामिल है, जो इसके उपयोग को सीमित करता है।
उदाहरण के लिए, एक विज्ञापन:
var buffer [256]byte
एक
बफ़र चर बनाता है जिसमें 256 बाइट्स होते हैं।
बफर चर प्रकार में आकार शामिल है और यह इस तरह दिखता है:
[256] बाइट । 512 बाइट की एक सरणी प्रकार
[512] बाइट होगी ।
सरणी से संबंधित डेटा केवल तत्वों की एक सरणी है। योजनाबद्ध रूप से, मेमोरी में हमारा बफर कुछ इस तरह दिखाई देगा:
buffer: byte byte byte ... 256 times ... byte byte byte
यही है, चर में 256 बाइट डेटा है और इससे अधिक कुछ नहीं है। हम सामान्य अनुक्रमण सिंटैक्स
बफर [0] ,
बफर [1] का उपयोग करते हुए तत्वों तक पहुंच सकते हैं, और इसी तरह
बफर [255] (0 से 255 के सूचकांक में 256 तत्व शामिल हैं)। इस सीमा से आगे जाने का प्रयास करने से प्रोग्राम क्रैश हो जाएगा।
एक अंतर्निहित
लेन फ़ंक्शन है जो किसी सरणी, स्लाइस और कुछ अन्य प्रकारों में तत्वों की संख्या लौटाता है। जाहिर है क्या वास्तव में सरणी के लिए
लेन वापस आ जाएगी। हमारे मामले में,
लेन (बफर) 256 वापस आ जाएगी।
सरणियों के लिए, आप अपने आवेदन की जगह पा सकते हैं। उदाहरण के लिए, वे मेट्रिसेस को बदलने के लिए अच्छे हैं, लेकिन गो में उनका सबसे आम उपयोग स्लाइस का भंडारण है।
स्लाइस: स्लाइस हैडर
स्लाइस जहां कुछ दिलचस्प होता है, लेकिन इससे पहले कि आप उनका उपयोग करना शुरू करें, आपको उनकी ज़रूरत को समझना होगा और वे क्या कर रहे हैं।
एक स्लाइस एक डेटा संरचना है जो एक सरणी के कई विभाजन का वर्णन करती है और एक चर से अलग संग्रहीत होती है।
एक टुकड़ा एक सरणी नहीं है । एक टुकड़ा एक सरणी का हिस्सा
वर्णन करता है ।
यदि हम पिछले अनुभाग से
बफर सरणी लेते हैं, तो हम एक स्लाइस बना सकते हैं जो 100 से 150 तक तत्वों का वर्णन करेगा (सटीक होने के लिए, 100 से 149 समावेशी) सरणी को
टुकड़ा करके :
var slice []byte = buffer[100:150]
इस कोड के टुकड़े में, सटीक होने के लिए, हमने पूर्ण चर घोषणा का उपयोग किया।
स्लाइस चर प्रकार
[] बाइट का है , जिसे "बाइट्स का स्लाइस" के रूप में पढ़ा जाता है और
बफर ऐरे से तत्व 100 (समावेशी) से 150 (विशेष रूप से) तक स्लाइस करके बनाया जाता है। अधिक "कैनोनिकल" सिंटैक्स में, हम उस प्रकार को छोड़ देंगे, जिसे आरंभीकरण प्रक्रिया के दौरान परिभाषित किया जाएगा:
var slice = buffer[100:150]
और फ़ंक्शन के अंदर, हम घोषणा के एक छोटे रूप का उपयोग करेंगे:
slice := buffer[100:150]
एक टुकड़ा क्या है? यह एक पूर्ण विवरण नहीं है, लेकिन अब से, दो तत्वों से मिलकर एक छोटी संरचना के रूप में एक स्लाइस के बारे में सोचें: एक सरणी तत्व के लिए एक लंबाई और एक सूचक। पर्दे के पीछे इस तरह से विचार करें:
type sliceHeader struct { Length int ZerothElement *byte } slice := sliceHeader{ Length: 50, ZerothElement: &buffer[100], }
बेशक, यह सिर्फ एक दृष्टांत है। इसके बावजूद, कोई इस उदाहरण से समझ सकता है कि
स्लाइसहेडर संरचना प्रोग्रामर के लिए सुलभ नहीं है, और पॉइंटर का प्रकार तत्वों के प्रकार पर निर्भर करता है, लेकिन यह स्लाइसिंग के यांत्रिकी के मूल विचार को समझना संभव बनाता है।
अब तक, हमने एक सरणी को टुकड़ा करने की क्रिया का उपयोग किया है, लेकिन हम टुकड़ा भी काट सकते हैं:
slice2 := slice[5:10]
पहले की तरह ही, यह ऑपरेशन एक नया टुकड़ा बनाता है, लेकिन मूल स्लाइस के सापेक्ष 5 से 9 (समावेशी) तत्वों के मामले में, जिसका अर्थ मूल सरणी के 105 से 109 के तत्व हैं।
Slice2 चर के लिए मूल
स्लाइसहेडर संरचना इस तरह दिखाई देगी:
slice2 := sliceHeader{ Length: 5, ZerothElement: &buffer[105], }
ध्यान दें कि हेडर अभी भी
बफर चर में स्थित अंतर्निहित सरणी को इंगित करता है।
हम
फिर से कट भी कर सकते हैं, जिसका अर्थ है कि एक स्लाइस में कटौती और कट स्लाइस की संरचना में परिणाम को बचाने के लिए। यानी निम्नलिखित:
slice = slice[5:10]
sliceHeader संरचना
स्लाइस चर के लिए
slice2 चर के रूप में एक ही दिखेगा। उदाहरण के लिए, एक स्लाइस काटने के लिए आप अक्सर ओवरशूट देखेंगे। इस उदाहरण में, हमारे स्लाइस के पहले और अंतिम तत्व को छोड़ दिया जाएगा:
slice = slice[1:len(slice)-1]
आप अक्सर अनुभवी गो प्रोग्रामर से "स्लाइस हेडर" के बारे में सुन सकते हैं क्योंकि यह वह है जो स्लाइस चर में संग्रहीत है। उदाहरण के लिए, जब आप किसी फ़ंक्शन को एक तर्क के रूप में स्लाइस लेते हैं, जैसे कि
बाइट्स। IndexRune , तो एक हेडर फ़ंक्शन को दिया जाएगा। इस उदाहरण में:
slashPos := bytes.IndexRune(slice, '/')
स्लाइस तर्क को
IndexRune फ़ंक्शन में भेजा जाएगा और वास्तव में, यह केवल "स्लाइस हैडर" है।
"स्लाइस हेडर" में एक और डेटा तत्व है जिसके बारे में हम नीचे बात करेंगे, लेकिन पहले, आइए एक नज़र डालते हैं कि "स्लाइस हेडर" का अर्थ क्या है जब आप एक प्रोग्राम लिखते हैं जो स्लाइसर्स का उपयोग करता है।
स्लाइस को फंक्शन्स में पास करना
यह समझना बहुत महत्वपूर्ण है कि भले ही स्लाइस में पॉइंटर हो, यह अपने आप में एक मूल्य है। हुड के तहत, यह एक संरचना है जिसमें एक सूचक और एक लंबाई होती है। यह किसी संरचना का सूचक
नहीं है ।
यह महत्वपूर्ण है।
जब हम पिछले उदाहरण में
IndexRune कहते हैं, तो यह "शीर्ष लेख के शीर्ष"
की एक
प्रति लेता है। इस व्यवहार का एक महत्वपूर्ण परिणाम है।
एक साधारण कार्य पर विचार करें:
func AddOneToEachElement(slice []byte) { for i := range slice { slice[i]++ } }
वह वही करता है जो शीर्षक में लिखा गया है, अर्थात्। सभी स्लाइस तत्वों (
रेंज लूप के लिए) का उपयोग करके, इसके तत्वों को बढ़ाता है।
प्रयास करें:
func main() { slice := buffer[10:20] for i := 0; i < len(slice); i++ { slice[i] = byte(i) } fmt.Println("before", slice) AddOneToEachElement(slice) fmt.Println("after", slice) }
इस तथ्य के बावजूद कि फ़ंक्शन को "
स्लाइस हेडर " पास किया गया है, इसमें सरणी के तत्वों के लिए एक सूचक शामिल है, इसलिए मूल स्लाइस हेडर और इसकी प्रतिलिपि उसी सरणी का वर्णन करती है। नतीजतन, जब फ़ंक्शन समाप्त होता है, तो बदले हुए तत्व मूल टुकड़ा के माध्यम से देखे जा सकते हैं।
फ़ंक्शन का तर्क वास्तव में एक प्रति है, और यह उदाहरण यह दिखाता है:
func SubtractOneFromLength(slice []byte) []byte { slice = slice[0 : len(slice)-1] return slice } func main() { fmt.Println("Before: len(slice) =", len(slice)) newSlice := SubtractOneFromLength(slice) fmt.Println("After: len(slice) =", len(slice)) fmt.Println("After: len(newSlice) =", len(newSlice)) }
यहां हम देखते हैं कि फ़ंक्शन के माध्यम से तर्क की
सामग्री को बदला जा सकता है, लेकिन इसका
शीर्षक नहीं। लंबाई को
स्लाइस चर में संग्रहीत किया जाता है और फ़ंक्शन को कॉल करने के परिणामस्वरूप नहीं बदलता है, क्योंकि स्लाइस हेडर की एक प्रति फ़ंक्शन को पास की जाती है, मूल नहीं। इस प्रकार, यदि हम एक फ़ंक्शन लिखना चाहते हैं जो शीर्षक को बदलता है, तो हमें इसे वापस करना होगा, जैसा कि हमने उदाहरण में किया था।
स्लाइस वेरिएबल में बदलाव नहीं होता है, लेकिन रिटर्न वैल्यू की एक नई लंबाई होती है, जो
न्यूस्लीस में संग्रहित
होती है ।
स्लाइसर पॉइंटर्स: प्रोडक्शन मेथड्स
एक फ़ंक्शन लिखने का एक और तरीका है जो स्लाइस हेडर को बदलता है, और यह एक पॉइंटर को फ़ंक्शन में स्लाइस के पास दे रहा है। इस विशेषता को प्रदर्शित करने के लिए हमारे उदाहरण का एक रूपांतर है:
func PtrSubtractOneFromLength(slicePtr *[]byte) { slice := *slicePtr *slicePtr = slice[0 : len(slice)-1] } func main() { fmt.Println("Before: len(slice) =", len(slice)) PtrSubtractOneFromLength(&slice) fmt.Println("After: len(slice) =", len(slice)) }
एक उदाहरण थोड़ा अजीब लग सकता है, जिसे अमूर्त (अस्थायी चर) का अतिरिक्त स्तर दिया गया है, लेकिन एक ऐसा मामला है जहां आप स्लाइस करने के लिए पॉइंटर्स का उपयोग करेंगे। यह एक स्लाइस को संशोधित करने वाली विधि लिखते समय एक रिसीवर के रूप में एक सूचक का उपयोग करने के लिए प्रथागत है।
मान लीजिए कि हम एक ऐसी विधि चाहते थे जो अंतिम स्लैश को समाप्त कर दे। हम इसे कुछ इस तरह लिख सकते हैं:
type path []byte func (p *path) TruncateAtFinalSlash() { i := bytes.LastIndex(*p, []byte("/")) if i >= 0 { *p = (*p)[0:i] } } func main() { pathName := path("/usr/bin/tso")
यदि आप उदाहरण चलाते हैं, तो आप देखेंगे कि जो आवश्यक है वह होगा, अर्थात विधि स्लाइस को बदल देगी।
दूसरी ओर, यदि हम
पथ के लिए एक विधि लिखना चाहते हैं जो ASCII वर्णों के ऊपरी मामले को निर्धारित करता है (अंग्रेजी वर्णों के साथ कोई व्यवहार परिभाषित नहीं है), विधि एक मान के साथ काम कर सकती है, न कि सूचक के कारण, क्योंकि रिसीवर मान अभी भी उस सरणी को इंगित करता है।
type path []byte func (p path) ToUpper() { for i, b := range p { if 'a' <= b && b <= 'z' { p[i] = b + 'A' - 'a' } } } func main() { pathName := path("/usr/bin/tso") pathName.ToUpper() fmt.Printf("%s\n", pathName) }
यहाँ,
ToUpper विधि तत्व के सूचकांक का उपयोग करने के
लिए रेंज में दो वैरिएबल्स का उपयोग करती है और, सीधे,
स्लैम एलिमेंट का उपयोग करती है। यह
पी [i] को फिर से लिखने से बचना होगा।
क्षमता
निम्नलिखित फ़ंक्शन पर विचार करें, जो एक तत्व द्वारा
चींटियों के स्लाइस को बढ़ाता है:
func Extend(slice []int, element int) []int { n := len(slice) slice = slice[0 : n+1] slice[n] = element return slice }
अब चलाएं:
func main() { var iBuffer [10]int slice := iBuffer[0:0] for i := 0; i < 20; i++ { slice = Extend(slice, i) fmt.Println(slice) } }
आइए देखें कि स्लाइस कैसे बढ़ता है ... यह बढ़ता है।
यह स्लाइस हैडर के तीसरे घटक के बारे में बात करने का समय है: इसकी
क्षमता । सरणी और उसकी लंबाई के सूचक के अलावा, स्लाइस हैडर में इसकी क्षमता होती है:
type sliceHeader struct { Length int Capacity int ZerothElement *byte }
क्षमता क्षेत्र में यह रिकॉर्ड होता है कि सरणी वास्तव में कितनी जगह पर है - यह अधिकतम मूल्य है जो
लंबाई तक पहुंच सकता है। अपनी क्षमता से ऊपर कटौती को बढ़ाने के प्रयास से सरणी का बहिर्वाह होगा और आपातकालीन कार्यक्रम समाप्ति का कारण होगा।
यह उदाहरण एक स्लाइस बनाता है
slice := iBuffer[0:0]
और इसका शीर्षक ऐसा दिखता है:
slice := sliceHeader{ Length: 0, Capacity: 10, ZerothElement: &iBuffer[0], }
क्षमता फ़ील्ड मूल सरणी की लंबाई के बराबर है, जो सरणी तत्व का सूचकांक घटा है, जो पहला टुकड़ा तत्व है (इस मामले में, शून्य)। यदि आप स्लाइस की क्षमता जानना चाहते हैं, तो
कैप फ़ंक्शन का उपयोग करें:
if cap(slice) == len(slice) { fmt.Println("slice is full!") }
मेक
क्या होगा अगर हम अपनी क्षमता से अधिक कटौती बढ़ाना चाहते हैं? हम नहीं कर सकते! परिभाषा के अनुसार, क्षमता वृद्धि की सीमा है। लेकिन आप एक नया सरणी बनाकर, डेटा की प्रतिलिपि बनाकर और नए सरणी का वर्णन करने वाले स्लाइस को बदलकर एक ही परिणाम प्राप्त कर सकते हैं।
आइए हाइलाइट करके शुरू करें। हम एक बड़े सरणी को आवंटित करने और एक बड़े स्लाइस में परिणाम के लिए
नए फ़ंक्शन का उपयोग कर सकते हैं, लेकिन
मेक फ़ंक्शन का उपयोग करना आसान होगा। यह एक नई सरणी का चयन करता है और एक स्लाइस हैडर बनाता है।
मेक फंक्शन में तीन तर्क होते हैं: स्लाइस प्रकार, प्रारंभिक लंबाई और इसकी क्षमता, जहां एरे की लंबाई वह होती है जो स्लाइस डेटा के लिए आवंटित करता है। नतीजतन, यह फ़ंक्शन कॉल लंबाई 10 का स्लाइस और 5 (15-10) तक विस्तार की संभावना बनाता है, जिसे आप इसे चलाने के बाद देख सकते हैं:
slice := make([]int, 10, 15) fmt.Printf("len: %d, cap: %d\n", len(slice), cap(slice))
यह स्निपेट हमारे
अंतर स्लाइस की क्षमता को दोगुना कर देता है, लेकिन समान लंबाई छोड़ देता है:
slice := make([]int, 10, 15) fmt.Printf("len: %d, cap: %d\n", len(slice), cap(slice)) newSlice := make([]int, len(slice), 2*cap(slice)) for i := range slice { newSlice[i] = slice[i] } slice = newSlice fmt.Printf("len: %d, cap: %d\n", len(slice), cap(slice))
उसके बाद, स्लाइस में वृद्धि के लिए बहुत अधिक जगह है इससे पहले कि इसे एक और पुनर्वितरण की आवश्यकता हो।
स्लाइस बनाते समय, अक्सर ऐसा होता है कि लंबाई और क्षमता एक समान होती है।
मेक फंक्शन का छोटा संस्करण है। डिफ़ॉल्ट लंबाई क्षमता बन जाती है, इसलिए आप उन्हें एक ही मूल्य में निर्दिष्ट कर सकते हैं। के बाद
gophers := make([]Gopher, 10)
गोफर्स स्लाइस में
, लंबाई और क्षमता 10 होगी।
नकल
पिछले अनुभाग में हमने अपने स्लाइस की क्षमता दोगुनी करने के बाद, पुराने डेटा को एक नए स्लाइस में कॉपी करने के लिए लूप को फिर से लिखा। गो में एक अंतर्निहित
कॉपी फ़ंक्शन है जो इस कार्य को सरल करता है। उसके तर्क दो स्लाइस हैं और वह डेटा को दाईं ओर से बाईं ओर स्लाइस पर कॉपी करता है।
कॉपी का उपयोग करने के लिए हमारा उदाहरण फिर से लिखा गया
है :
newSlice := make([]int, len(slice), 2*cap(slice)) copy(newSlice, slice)
कॉपी फंक्शन स्मार्ट है। यह केवल वही कर सकता है जो यह कर सकता है, दोनों तर्कों की लंबाई पर ध्यान देना। दूसरे शब्दों में, कॉपी किए जाने वाले तत्वों की संख्या दोनों स्लाइस की लंबाई के न्यूनतम के बराबर है। इससे थोड़ी "नौकरशाही" बच सकती है। इसके अलावा,
कॉपी एक पूर्णांक मान लौटाता है - जिन तत्वों की प्रतिलिपि बनाई गई थी, हालांकि यह हमेशा जांचने योग्य नहीं है।
कॉपी फंक्शन भी मामलों को ध्यान में रखता है जब स्रोत और रिसीवर इंटरसेक्ट (लगभग। ट्रांस। यह सी में मेमोव () की तरह होता है), जिसका अर्थ है कि फ़ंक्शन का उपयोग एक स्लाइस के अंदर तत्वों को स्थानांतरित करने के लिए किया जा सकता है। नीचे एक स्लाइस के बीच में एक मान पेस्ट करने के लिए कॉपी फ़ंक्शन का उपयोग करने का एक उदाहरण है।
इस सुविधा में कई बिंदु हैं जिन्हें आप देख सकते हैं। सबसे पहले, जो स्पष्ट है, उसे टुकड़ा वापस करना होगा, क्योंकि इसकी लंबाई बदल गई है। दूसरे, एक सुविधाजनक संकुचन का उपयोग किया जाता है। अभिव्यक्ति
slice[i:]
का मतलब वही है
slice[i:len(slice)]
इसके अलावा, हमने एक और चाल का उपयोग नहीं किया, हम अभिव्यक्ति के पहले तत्व को भी खाली छोड़ सकते हैं; डिफ़ॉल्ट रूप से यह शून्य होगा। इस तरह से
slice[:]
इसका सीधा मतलब है कि खुद को स्लाइस करना, जो किसी ऐरे को स्लाइस करते समय उपयोगी है। यह अभिव्यक्ति सबसे छोटा शब्द है: "सरणी के सभी तत्वों का वर्णन करने वाला टुकड़ा":
array[:]
लेकिन यह मामलों के बीच में था, चलो हमारे
सम्मिलित फ़ंक्शन का प्रयास
करें ।
slice := make([]int, 10, 20)
सम्मिलित करें: उदाहरण
कुछ खंडों से पहले, हमने एक
एक्सटेंड फ़ंक्शन लिखा था जिसमें एक तत्व द्वारा एक टुकड़ा बढ़ाया गया था। यह गलत था, यदि केवल इस कारण से कि जब स्लाइस की क्षमता बहुत छोटी थी, तो फ़ंक्शन विफल हो सकता है (हमारे उदाहरण में,
इन्सर्ट फ़ंक्शन उसी समस्या के अधीन है)। अब हम जानते हैं कि इसे कैसे ठीक किया जाए, तो आइए पूर्णांक स्लाइस के लिए
विस्तार फ़ंक्शन का एक विश्वसनीय कार्यान्वयन लिखें।
func Extend(slice []int, element int) []int { n := len(slice) if n == cap(slice) {
इस मामले में, स्लाइस को वापस करना विशेष रूप से महत्वपूर्ण है, क्योंकि जब हमने इसे पुनर्वितरित किया था, तो हमें जो स्लाइस मिला था, वह पूरी तरह से अलग सरणी का वर्णन करता है। यहाँ प्रदर्शित करने के लिए एक छोटा सा टुकड़ा है यदि टुकड़ा भर जाता है तो क्या होता है:
slice := make([]int, 0, 5) for i := 0; i < 10; i++ { slice = Extend(slice, i) fmt.Printf("len=%d cap=%d slice=%v\n", len(slice), cap(slice), slice) fmt.Println("address of 0th element:", &slice[0]) }
5 के आकार की प्रारंभिक सरणी भर जाने पर पुनर्वितरण पर ध्यान दें। एक नया सरणी आवंटित होने पर नल आइटम की क्षमता और पता बदल जाता है।
आधार के रूप में मजबूत
विस्तार समारोह का उपयोग करना, हम एक और भी बेहतर कार्य लिख सकते हैं जो हमें कई तत्वों के साथ स्लाइस का विस्तार करने की अनुमति देता है। ऐसा करने के लिए, तर्क की सूची को एक सरणी में बदलने के लिए गो की क्षमता का लाभ उठाएं। यही है, हम गो की क्षमता का उपयोग कर तर्कों की एक चर संख्या का उपयोग करते हैं।
चलो फ़ंक्शन को कहते हैं। पहले संस्करण के लिए, हम केवल तब तक कॉल कर सकते हैं जब तक कि फ़ंक्शन तर्क समाप्त नहीं हो जाते।
परिशिष्ट फ़ंक्शन का प्रोटोटाइप इस तरह दिखता है:
func Append(slice []int, items ...int) []int
यह हमें बताता है कि
अपेंड एक तर्क - एक स्लाइस लेता है, और यह शून्य से अनंत प्रकार के तर्कों के अनंत तक का अनुसरण करता है। ये तत्व भविष्य के स्लाइस स्लाइस हैं, जैसा कि आप देख सकते हैं:
ध्यान दें कि
रेंज लूप के लिए
आइटम तर्क के प्रत्येक तत्व के लिए निष्पादित किया जाता है, जो टाइप
[] int का है । इसके अलावा, खाली पहचानकर्ता _ के उपयोग पर ध्यान दें, जो सूचकांक को त्यागता है, क्योंकि हमें लूप में इसकी आवश्यकता नहीं है।
इसे आज़माएँ:
slice := []int{0, 1, 2, 3, 4} fmt.Println(slice) slice = Append(slice, 5, 6, 7, 8) fmt.Println(slice)
इस उदाहरण में एक और नई चाल यह है कि स्लाइस इनिशियलाइज़ेशन एक समग्र शाब्दिक के साथ किया जाता है, जिसमें ब्रेस में संलग्न प्रकार और स्लाइस तत्व होते हैं:
slice := []int{0, 1, 2, 3, 4}
दूसरे कारण से भी दिलचस्प है। हम केवल तत्वों को जोड़ नहीं सकते, हम पूरे स्लाइस को फ़ंक्शन के तर्क के रूप में उपयोग कर सकते हैं ...:
slice1 := []int{0, 1, 2, 3, 4} slice2 := []int{55, 66, 77} fmt.Println(slice1) slice1 = Append(slice1, slice2...)
बेशक, हम
विस्तार के आधार पर एकल चयन द्वारा
परिशिष्ट को अधिक कुशल बना सकते हैं:
ध्यान दें कि यहां हम डेटा स्लाइस को मेमोरी के एक नए टुकड़े में स्थानांतरित करने के लिए दो बार
कॉपी का उपयोग करते हैं और फिर पुराने डेटा के अंत में जोड़े गए आइटम की प्रतिलिपि बनाते हैं।
यह कोशिश करो, व्यवहार पहले जैसा है:
slice1 := []int{0, 1, 2, 3, 4} slice2 := []int{55, 66, 77} fmt.Println(slice1) slice1 = Append(slice1, slice2...)
परिशिष्ट: निर्मित समारोह
तो, हम इस निष्कर्ष पर पहुंचे कि गो में आपको बिल्ट-इन फंक्शन
एपेंड को जोड़ने की जरूरत है। यह एक ही कार्यकुशलता के साथ उदाहरण के रूप में हमारे
परिशिष्ट कार्य करता है, लेकिन किसी भी प्रकार के स्लाइसर के लिए काम करता है।
गो की कमजोरी यह है कि "जेनेरिक प्रकार" पर परिभाषित कोई भी ऑपरेशन रन टाइम पर प्रदान किया जाना चाहिए। किसी दिन यह बदल सकता है, लेकिन अब, स्लाइसर्स के साथ काम को आसान बनाने के लिए, गो एक बिल्ट-इन सामान्य फ़ंक्शन
एपेंडेंस प्रदान करता है। यह हमारे पूर्णांक संस्करण के समान ही काम करता है, लेकिन
किसी भी प्रकार के स्लाइस के लिए।
याद रखें कि चूंकि स्लाइस हैडर को हर बार
अपेंडेड कॉल करने के बाद अपडेट किया जाता है, इसलिए आपको कॉल के बाद परिणामी स्लाइस को सहेजना होगा। वास्तव में, संकलक आपको परिणाम को बचाने के बिना
एपेंड को कॉल करने की अनुमति नहीं देगा।
यहाँ कुछ सिंगल-लाइन आउटपुट हैं। उन्हें आज़माएं, बदलें और देखें:
आपको उदाहरण की अंतिम पंक्तियों के बारे में सोचना और रोकना चाहिए और यह समझना चाहिए कि कैसे स्लाइस का डिज़ाइन आपको इस तरह के सरल कॉल को सही बनाने की अनुमति देता है।
स्लाइस ट्रिक्स विकी (समुदाय-निर्मित) पर कई उदाहरण हैं, जो
एपेंड ,
कॉपी और स्लाइस का उपयोग करने के अन्य तरीकों का उपयोग करते हैं।
शून्य
इसके अलावा, नए अधिग्रहीत ज्ञान का उपयोग करके, हम समझ सकते हैं कि "नील" कट क्या है। स्वाभाविक रूप से, यह स्लाइस हैडर का शून्य मान है:
sliceHeader{ Length: 0, Capacity: 0, ZerothElement: nil, }
या बस
sliceHeader{}
कुंजी यह है कि सूचक भी
शून्य है । यह स्लाइस
array[0:0]
शून्य लंबाई (और शायद शून्य क्षमता भी है), लेकिन इसका सूचक
शून्य नहीं है, इसलिए यह अभी भी शून्य टुकड़ा नहीं है।
जाहिर है, एक खाली टुकड़ा विकसित हो सकता है (यह मानते हुए कि यह शून्य क्षमता का नहीं है), लेकिन "शून्य" (
एनआईएल ) टुकड़ा में एक सरणी नहीं होती है जहां आप मान डाल सकते हैं और बढ़ नहीं सकते हैं, यहां तक कि कम से कम एक तत्व शामिल करने के लिए नहीं।
यह ध्यान देने योग्य है कि एक "शून्य" टुकड़ा अनिवार्य रूप से एक शून्य-लंबाई वाले स्लाइस के बराबर है, भले ही वह कुछ भी इंगित नहीं करता हो। इसकी लंबाई शून्य है और आप इसमें चयन के साथ कुछ जोड़ सकते हैं।
उदाहरण के लिए, ऊपर कुछ पंक्तियां, जिसमें टुकड़ा जोड़ने "शून्य» (द्वारा कॉपी किया जाता है पर नज़र नहीं के बराबर ) टुकड़ा।पंक्तियां
अब संक्षेप में गो के स्लाइस के संदर्भ में लाइनों के बारे में।दरअसल, स्ट्रिंग्स बहुत सरल हैं: ये केवल पढ़ने वाली बाइट स्लाइस हैं, भाषा से थोड़ा अतिरिक्त सिंटैक्टिक समर्थन के साथ।चूंकि वे केवल पढ़ने के लिए हैं, उनके पास क्षमता नहीं है (आप उन्हें बढ़ा नहीं सकते हैं), हालांकि ज्यादातर मामलों में आप उन्हें बाइट्स के स्लाइस के रूप में मान सकते हैं।शुरुआत के लिए, हम अलग-अलग बाइट्स तक पहुंचने के लिए अनुक्रमण का उपयोग कर सकते हैं: slash := "/usr/ken"[0]
सबस्ट्रिंग बनाने के लिए हम एक स्ट्रिंग को स्लाइस कर सकते हैं: usr := "/usr/ken"[0:4]
यह स्पष्ट होना चाहिए कि जब हम स्ट्रिंग काटते हैं तो पर्दे के पीछे क्या होता है।इसलिए हम एक साधारण बाइट स्लाइस ले सकते हैं और एक साधारण रूपांतरण द्वारा इसे एक स्ट्रिंग बना सकते हैं: str := string(slice)
साथ ही स्ट्रिंग से बाइट्स का एक टुकड़ा बनाते हैं: slice := []byte(usr)
तार को अंतर्निहित सरणी देखने से छिपी हुई है, एक स्ट्रिंग के अलावा इसे एक्सेस करने का कोई तरीका नहीं है। इसका मतलब यह है कि जब हम इन परिवर्तनों को करते हैं, तो सरणी की एक प्रति बनाई जानी चाहिए। जा काम लेता है, इसलिए इसके बारे में चिंता मत करो। ऐसे किसी भी रूपांतरण के बाद, अंतर्निहित बाइट स्लाइस के सरणी को बदलने से संबंधित लाइन प्रभावित नहीं होती है।स्लाइस के रूप में तार के समान व्यवहार का एक महत्वपूर्ण परिणाम यह है कि एक विकल्प बनाना बहुत कुशल है। सभी आवश्यक है कि लाइनों के दो सबसे ऊपर का निर्माण हो। चूंकि स्ट्रिंग केवल-पढ़ने के लिए है, मूल स्ट्रिंग और स्लिंग के परिणामस्वरूप प्राप्त स्ट्रिंग सुरक्षित रूप से एक ही सरणी साझा कर सकती है।ऐतिहासिक नोट: प्रारंभिक स्ट्रिंग कार्यान्वयन हमेशा अलग-अलग खड़े होते थे, लेकिन तब से भाषा में स्लाइस दिखाई देते हैं, जिससे स्ट्रिंग्स के साथ अधिक कुशल काम बनाना संभव हो गया। कुछ मानदंड गति में भारी वृद्धि दिखाने लगे।बेशक, लाइनों के बारे में बताने के लिए बहुत कुछ है, लेकिन यह विषय एक और प्रकाशन के लिए है।निष्कर्ष
स्लाइसिंग के सिद्धांतों को समझना, यह समझने में मदद करता है कि वे कैसे बने हैं। एक छोटी डेटा संरचना है, स्लाइस हेडिंग एक स्लाइस टाइप वैरिएबल से जुड़ी होती है, और यह हेडिंग एक अलग से आवंटित एरे का हिस्सा होती है। जब हम एक स्लाइस बनाते हैं, तो हेडर कॉपी किया जाता है, लेकिन सरणी हमेशा विभाजित होती है।जब आप उनके काम का मूल्यांकन करते हैं, तो स्लाइस न केवल उपयोग करना आसान हो जाएगा, बल्कि शक्तिशाली और अभिव्यंजक होगा, विशेष रूप से अंतर्निहित कॉपी और अपील कार्यों के साथ ।
मूल प्रकाशन - blog.golang.org/slices