मैं `प्रति ()` के साथ एक स्लाइस की नकल क्यों नहीं कर सकता?


121

मुझे गो में एक स्लाइस की प्रतिलिपि बनाने की आवश्यकता है और डॉक्स पढ़ने से मेरे निपटान में एक कॉपी फ़ंक्शन है।

एक अंतर्निहित स्लाइस से तत्वों को एक गंतव्य स्लाइस में कॉपी करता है। (एक विशेष मामले के रूप में, यह बाइट्स को एक स्ट्रिंग से बाइट्स के स्लाइस में भी कॉपी करेगा।) स्रोत और गंतव्य ओवरलैप हो सकता है। कॉपी किए गए तत्वों की संख्या की प्रतिलिपि देता है, जो कि लेन (src) और लेन (dst) की न्यूनतम होगी।

लेकिन जब मैं करता हूं:

arr := []int{1, 2, 3}
tmp := []int{}
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)

मेरा tmpखाली है जैसा कि पहले था (मैंने भी उपयोग करने की कोशिश की arr, tmp):

[]
[1 2 3]

आप इसे खेल के मैदान पर देख सकते हैं । तो मैं एक स्लाइस की नकल क्यों नहीं कर सकता?


सभी को धन्यवाद, यह वास्तव में दुखद है कि मैंने ध्यान नहीं दिया है कि स्लाइस समान लंबाई होनी चाहिए।
साल्वाडोर डाली

1
जरूरी नहीं कि समान हो, लेकिन dstकम से कम उतने ही बड़े होने चाहिए जितने तत्व आप कॉपी करना चाहते हैं (पूरी कॉपी के लिए srcइसका मतलब है len(dst) >= len(src))।
आईसीएजे

2
b := append([]int{}, a...)
रॉकेट्सपेसर

जवाबों:


209

बिलिन तत्वों की copy(dst, src)प्रतिलिपि बनाता min(len(dst), len(src))है।

इसलिए यदि आपका dstखाली है ( len(dst) == 0), तो कुछ भी कॉपी नहीं किया जाएगा।

कोशिश करो tmp := make([]int, len(arr))( खेल का मैदान जाओ ):

arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)

आउटपुट (उम्मीद के मुताबिक):

[1 2 3]
[1 2 3]

दुर्भाग्य से यह builtinपैकेज में प्रलेखित नहीं है , लेकिन इसे गो भाषा विशिष्टता में प्रलेखित किया गया है : स्लाइस के लिए आवेदन करना और उसकी प्रतिलिपि बनाना :

कॉपी किया तत्वों की संख्या कम से कम है len(src)और len(dst)

संपादित करें:

अंत में प्रलेखन copy()अद्यतन कर दिया गया है और इसमें अब यह तथ्य शामिल है कि स्रोत और गंतव्य की न्यूनतम लंबाई की प्रतिलिपि बनाई जाएगी:

कॉपी किए गए तत्वों की संख्या की प्रतिलिपि देता है, जो कि लेन (src) और लेन (dst) की न्यूनतम होगी ।


2
संक्षेप में, copyगंतव्य स्लाइस को बढ़ने के लिए तर्क शामिल नहीं है यदि गंतव्य स्लाइस बहुत छोटा है, लेकिन एक और अंतर्निहित फ़ंक्शन है जो करता है: append जबकि इस उदाहरण में यह बेहतर है कि पहले स्थान पर सही आकार का स्लाइस आवंटित करें, appendउपयोग किया जा सकता है जब आपके पास पहले से ही एक टुकड़ा है और इसे तत्वों को अंत तक जोड़ना चाहते हैं।
thomasrutter

1
लेकिन मुझे एक अनबाउंड आकार के स्लाइस की नकल करते समय एक बाउंडेड आकार का टुकड़ा क्यों बनाना पड़ता है?
एलेक्स

24

ऐसा करने का एक और सरल तरीका है appendजिसका उपयोग करके प्रक्रिया में टुकड़ा आवंटित किया जाएगा।

arr := []int{1, 2, 3}
tmp := append([]int(nil), arr...)  // Notice the ... splat
fmt.Println(tmp)
fmt.Println(arr)

आउटपुट (उम्मीद के मुताबिक):

[1 2 3]
[1 2 3]

तो सरणी की नकल के लिए एक आशुलिपि arrहोगीappend([]int(nil), arr...)

https://play.golang.org/p/sr_4ofs5GW


8
यहां पकड़ यह है कि वास्तविक दुनिया के उदाहरणों में, जो बहुत बड़े हैं, परिशिष्ट अतिरिक्त मेमोरी आवंटित करेगा - जब तक कि इस सरणी को बाद में कुछ और प्रसंस्करण द्वारा क्षमता से भरा नहीं जाता है - क्योंकि यह बार-बार कॉल पर कुशल reallocation के लिए डिज़ाइन किया गया है। play.golang.org/p/5_6618xnXn निरीक्षण करें कि टोपी (x) 12 तक बढ़ती है, 10. नहीं। अब देखो क्या होता है जब 1 मूल्य 1048576 मूल्यों में जोड़ा जाता है। play.golang.org/p/nz32Jnhh क्षमता 2048 स्लॉट्स द्वारा कूदता है 1050624, केवल एक अतिरिक्त मूल्य को समायोजित करने के लिए।
जे। andrew shusta

12

यदि आपके स्लाइस समान आकार के होते हैं, तो यह काम करेगा :

arr := []int{1, 2, 3}
tmp := []int{0, 0, 0}
i := copy(tmp, arr)
fmt.Println(i)
fmt.Println(tmp)
fmt.Println(arr)

देना होगा:

3
[1 2 3]
[1 2 3]

" गो स्लाइस: यूसेज एंड इंटर्नल ":

कॉपी फ़ंक्शन विभिन्न लंबाई के स्लाइस के बीच कॉपी करने का समर्थन करता है ( यह केवल तत्वों की कम संख्या तक कॉपी करेगा )

सामान्य उदाहरण है:

t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t

10

प्रतिलिपि () dst और src की कम से कम लंबाई के लिए चलती है, इसलिए आपको dst को वांछित लंबाई से प्रारंभ करना होगा।

A := []int{1, 2, 3}
B := make([]int, 3)
copy(B, A)
C := make([]int, 2)
copy(C, A)
fmt.Println(A, B, C)

आउटपुट:

[1 2 3] [1 2 3] [1 2]

आप एक निल स्लाइस के लिए एपेंड () का उपयोग करके सभी तत्वों को एक पंक्ति में प्रारंभिक और कॉपी कर सकते हैं।

x := append([]T{}, []...)

उदाहरण:

A := []int{1, 2, 3}
B := append([]int{}, A...)
C := append([]int{}, A[:2]...)
fmt.Println(A, B, C)    

आउटपुट:

[1 2 3] [1 2 3] [1 2]

1,000 से अधिक तत्वों के लिए आवंटन + कॉपी () के साथ तुलना करें, एपेंड का उपयोग करें। वास्तव में 1,000 अंतर को नजरअंदाज किया जा सकता है, इसे तब तक अंगूठे के नियम के लिए जाना है जब तक कि आपके पास कई स्लाइस न हों।

BenchmarkCopy1-4                50000000            27.0 ns/op
BenchmarkCopy10-4               30000000            53.3 ns/op
BenchmarkCopy100-4              10000000           229 ns/op
BenchmarkCopy1000-4              1000000          1942 ns/op
BenchmarkCopy10000-4              100000         18009 ns/op
BenchmarkCopy100000-4              10000        220113 ns/op
BenchmarkCopy1000000-4              1000       2028157 ns/op
BenchmarkCopy10000000-4              100      15323924 ns/op
BenchmarkCopy100000000-4               1    1200488116 ns/op
BenchmarkAppend1-4              50000000            34.2 ns/op
BenchmarkAppend10-4             20000000            60.0 ns/op
BenchmarkAppend100-4             5000000           240 ns/op
BenchmarkAppend1000-4            1000000          1832 ns/op
BenchmarkAppend10000-4            100000         13378 ns/op
BenchmarkAppend100000-4            10000        142397 ns/op
BenchmarkAppend1000000-4            2000       1053891 ns/op
BenchmarkAppend10000000-4            200       9500541 ns/op
BenchmarkAppend100000000-4            20     176361861 ns/op

1
एपेंड का उपयोग उन मामलों में किया जाना चाहिए जहां बार-बार कॉल करने से सरणी बढ़ जाएगी, क्योंकि यह इस की प्रत्याशा में अतिरिक्त क्षमता का आवंटन करेगा। उन मामलों में प्रति इनपुट सरणी में एक बार प्रतिलिपि का उपयोग किया जाना चाहिए जहां परिणाम सरणी को सटीक आकार में बनाया जाना चाहिए, और फिर से पुनः प्राप्त नहीं किया जाता है। play.golang.org/p/0kviwKmGzx आपने उन बेंचमार्क कोड को साझा नहीं किया, जो उन परिणामों का उत्पादन करते हैं ताकि मैं इसकी वैधता की पुष्टि या इनकार नहीं कर पाऊं , लेकिन यह इस अधिक महत्वपूर्ण पहलू की अनदेखी करता है।
जे। andrew shusta

1
आप 'टुकड़ा' नहीं सरणी का मतलब है । वे अलग चीजें हैं।
इनक गमस

2

जाओ प्रोग्रामिंग भाषा विशिष्टता

स्लाइस के लिए आवेदन करना और उसकी प्रतिलिपि बनाना

फ़ंक्शन कॉपी स्लाइस तत्वों को स्रोत src से गंतव्य गंतव्य तक कॉपी करता है और कॉपी किए गए तत्वों की संख्या लौटाता है। दोनों तर्कों में समान तत्व टाइप T होना चाहिए और टाइप [] T के एक स्लाइस के लिए असाइन किया जाना चाहिए। कॉपी किए गए तत्वों की संख्या लेन (src) और लेन (dst) की न्यूनतम है। एक विशेष मामले के रूप में, प्रतिलिपि स्ट्रिंग प्रकार के स्रोत तर्क के साथ [] बाइट टाइप करने के लिए एक गंतव्य तर्क को स्वीकार करती है। यह प्रपत्र बाइट्स को बाइट स्लाइस में स्ट्रिंग से कॉपी करता है।

copy(dst, src []T) int
copy(dst []byte, src string) int

tmpके लिए पर्याप्त जगह चाहिए arr। उदाहरण के लिए,

package main

import "fmt"

func main() {
    arr := []int{1, 2, 3}
    tmp := make([]int, len(arr))
    copy(tmp, arr)
    fmt.Println(tmp)
    fmt.Println(arr)
}

आउटपुट:

[1 2 3]
[1 2 3]

0

यहां एक स्लाइस को कॉपी करने का एक तरीका है। मुझे थोड़ी देर हो गई है, लेकिन @ डेव की तुलना में एक सरल और तेज़ उत्तर है। यह @ डेव्स जैसे कोड से उत्पन्न निर्देश है। ये मेरे द्वारा उत्पन्न निर्देश हैं। जैसा कि आप देख सकते हैं कि बहुत कम निर्देश हैं। क्या करता है यह सिर्फ करता है append(slice), जो टुकड़ा की नकल करता है। यह कोड:

package main

import "fmt"

func main() {
    var foo = []int{1, 2, 3, 4, 5}
    fmt.Println("foo:", foo)
    var bar = append(foo)
    fmt.Println("bar:", bar)
    bar = append(bar, 6)
    fmt.Println("foo after:", foo)
    fmt.Println("bar after:", bar)
}

इसे आउटपुट करता है:

foo: [1 2 3 4 5]
bar: [1 2 3 4 5]
foo after: [1 2 3 4 5]
bar after: [1 2 3 4 5 6]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.