Եթե դուք նոր եք սկսում Go-ի հետ, կարող եք լսել երկու նմանատիպ հնչյունավոր տվյալների մասին, որոնք կոչվում են «զանգվածներ» և «հատվածներ»: Առաջին հայացքից այս երկու տեսակները կարող են միմյանց փոխարինելի թվալ, բայց նրանք ունեն որոշ կարևոր տարբերություններ, որոնք արժե հասկանալ: Այս գրառման մեջ մենք ավելի մանրամասն կանդրադառնանք Go-ի զանգվածներին և հատվածներին և կբացատրենք, թե ինչն է դրանք առանձնացնում:
Զանգվածներ
Զանգվածը նույն տեսակի տարրերի ֆիքսված չափի հավաքածու է: Երբ զանգվածը հայտարարված է, դրա չափը չի կարող փոխվել:
var arr [5]int // this creates an array of 5 integers, with default values of 0
Շերտեր
Մյուս կողմից, Slice-ը դինամիկ փոփոխական երկարությամբ տեսք է զանգվածի մեջ: Հատված է ստեղծվում make()
ֆունկցիայի միջոցով, որն ընդունում է երկու արգումենտ՝ տարրի տեսակը և հատվածի սկզբնական չափը:
var s []int = make([]int, 5) // this creates a slice of 5 integers, with default values of 0
Հատվածները փոխանցվում են հղումով, ինչը նշանակում է, որ հատվածում կատարված ցանկացած փոփոխություն կազդի հիմնական զանգվածի վրա.
Մի վայրկյան սպասե՞լ: Ի՞նչ նկատի ունեք «հիմնական զանգված» ասելով:
Հատվածը չի պատկանում այն տվյալներին, որոնց վրա նա մատնանշում է, այն պարզապես դիտում է հիմքում ընկած զանգվածի մեջ: Սա նշանակում է, որ երբ հատվածը փոխանցվում է ֆունկցիայի, այն փոխանցվում է հղումով, և ֆունկցիայի շրջանակում կատարված ցանկացած փոփոխություն կարտացոլվի սկզբնական հատվածում: Ի հակադրություն, երբ զանգվածը փոխանցվում է ֆունկցիայի, այն փոխանցվում է արժեքով, ինչը նշանակում է, որ ֆունկցիայի ներսում զանգվածում կատարված ցանկացած փոփոխություն չի արտացոլվի սկզբնական զանգվածում։
Բայց մենք ասացինք, որ զանգվածները ֆիքսված չափի են, իսկ կտորները՝ դինամիկ, ապա ինչպե՞ս կարող է լինել, որ շերտերը դինամիկ են, եթե գլխարկի տակ երկուսն էլ զանգված են:
Հասկանալու համար, որ մենք պետք է հասկանանք մեկ այլ հասկացություն. Go-ում կտորն ունի և՛ երկարություն, և՛ տարողություն: Կտորի երկարությունը այն տարրերի քանակն է, որը ներկայումս պարունակում է, մինչդեռ կտորի տարողությունը այն տարրերի քանակն է, որոնք այն կարող է պարունակել: Կտորի հզորությունը որոշվում է հիմքում ընկած զանգվածի չափով, որին այն մատնանշում է: Երբ հատվածը ստեղծվում է make()
ֆունկցիայի միջոցով, հատվածի հզորությունը սահմանվում է նույն արժեքով, ինչ երկարությունը:
Այնուամենայնիվ, զանգվածների հակառակը, դուք կարող եք մեծացնել հատվածի հզորությունը՝ օգտագործելով append()
ֆունկցիան: Երբ դուք որևէ տարր եք ավելացնում հատվածին, Go-ն ստուգում է՝ արդյոք հատվածը բավարար կարողություն ունի նոր տարրը պահելու համար: Եթե կտորն ունի բավականաչափ հզորություն, նոր տարրը ավելացվում է կտորին, իսկ հատվածի երկարությունը մեծանում է 1-ով: պատճենում է տարրերը հին զանգվածից նոր զանգվածին, ավելացնում է նոր տարրը նոր զանգվածին և թարմացնում է հատվածը՝ նոր զանգվածին մատնանշելու համար: Այս գործընթացը կոչվում է վերաբաշխում:
Դուք կարող եք նաև հստակորեն սահմանել հատվածի հզորությունը՝ օգտագործելով cap()
ֆունկցիան:
s := make([]int, 5, 10) // create a slice of length 5 and capacity 10
Այս օրինակում դուք կարող եք օգտագործել append()
ֆունկցիան՝ կտորին տարրեր ավելացնելու մինչև իր 10 տարողությունը: Հենց որ գերազանցեք հատվածի հզորությունը, տեղի կունենա վերաբաշխում, և հատվածի հզորությունը կկրկնապատկվի մինչև 20:
Խորամանկ օրինակ
Հիմա տեսնենք, թե ինչ է տեղի ունենում, եթե հատվածին վերագրեք զանգված (կամ զանգվածի որոշ տարրեր):
arr := [5]int{1, 2, 3, 4, 5} slice := arr[1:4] // slice points to elements {2, 3, 4} of the array slice[0] = 10 // modify the first element of the slice fmt.Println(arr) // prints [1 10 3 4 5]
Երբ Go-ում զանգվածից հատված եք նշանակում, հատվածը և զանգվածը կիսում են նույն հիմքում ընկած տվյալները: Սա նշանակում է, որ եթե դուք փոփոխեք հատվածի տարրերը, ապա զանգվածի համապատասխան տարրերը նույնպես կփոփոխվեն։
Այս վարքագիծը կարող է օգտակար լինել որոշ դեպքերում, օրինակ, երբ ցանկանում եք ստեղծել գոյություն ունեցող զանգվածի ենթաբազմություն և փոփոխել այդ ենթաբազմության տարրերը: Այնուամենայնիվ, կարևոր է տեղյակ լինել այս մասին՝ շերտերի հետ աշխատելիս հիմքում ընկած տվյալները ակամա փոփոխելուց խուսափելու համար:
Եթե, մյուս կողմից, ցանկանում եք ստեղծել մի հատվածի պատճեն՝ առանց հիմքում ընկած տվյալները կիսելու բնօրինակ զանգվածի հետ, կարող եք օգտագործել copy()
ֆունկցիան՝ նոր հատված ստեղծելու և սկզբնական հատվածից տարրերը պատճենելու նոր հատվածում: Ահա մի օրինակ.
arr := [5]int{1, 2, 3, 4, 5} slice := arr[1:4] // slice points to elements {2, 3, 4} of the array copySlice := make([]int, len(slice)) copy(copySlice, slice) // copy the elements of slice into copySlice copySlice[0] = 10 // modify the first element of copySlice fmt.Println(arr) // prints [1 2 3 4 5], since the original array has not been modified
Հուսով եմ, որ այս հոդվածը ձեզ համար հետաքրքիր էր, և եթե ցանկանում եք ավելին իմանալ այն այլ թեմաների մասին, որոնցով ես կրքոտ եմ, ինչպիսիք են ծրագրավորումը, ամպը և ընդհանուր առմամբ SRE-ն, մի հապաղեք հետևել ինձ Medium-ում այս մեկի նման այլ պատմությունների համար 😃 — Ալեսիո Տրիվիսոննո
Շնորհավոր 🙌