قبل از آشنایی با slice لازمه که یک آشنایی اولیه با آرایه ها داشته باشید. برای تعریف یک آرایه باید طول و نوع آیتم های آرایه را مشخص کرد:
var x [5]int
x[0] = 10
x[1] = 7
x[2] = 35
fmt.Println(x) // [10 7 35 0]
مقادیر درون آرایه به صورت متوالی درون حافظه رم قرار میگیرند
به چند دلیل آرایه ها به صورت متداول مورد استفاده قرار نمیگیرند:
- طول آرایه بعد از اینکه آن را تعریف کنیم تغییر ناپذیر خواهد بود.
- طول آرایه بخشی از دیتا تایپ آرایه است برای مثال آرایه ی
[4]int
را نمیتوان به متغیری که نوع آن[5]int
نسبت داد.
برای تعریف اسلایس (slice decleration) از یک جفت کروشه [ ]
به همراه دیتا تایپ مورد نظر استفاده میکنیم:
package main
import "fmt"
func main() {
// اسلایس عددی
var y []int
// آرایه عددی با طول ۳
var x [3]int
fmt.Println(x) // [0 0 0]
fmt.Println(y) // []
}
در هنگام تعریف اسلایس همانند آرایه عمل میکنیم با این تفاوت که درون جفت کروشه [ ]
را خالی میگذاریم.
در Go, هر نوع دادهای یک مقدار پیشفرض به نام zero value (مقدار صفر) دارد که وقتی متغیری بدون مقداردهی اولیه تعریف میشود، به آن اختصاص داده میشود. این ویژگی باعث میشود که متغیرها در گولنگ همیشه مقدار معتبری داشته باشند و از خطاهای مرتبط با مقداردهی اولیه جلوگیری شود. مقدار صفر یک اسلایس (zero value) برابر با nil
است. با استفاده از دستور make
میتوان به اسلایس حافظه تخصیص داد.
package main
import "fmt"
func main() {
var y []int
y = make([]int, 3)
fmt.Println(y) // [0 0 0]
y[0] = 2
y[1] = 4
y[2] = 6
fmt.Println(y) // [2 4 6]
}
روش دیگر برای ایجاد اسلایس استفاده از :=
و تابع make
است:
package main
import "fmt"
func main() {
y := make([]int, 3)
fmt.Println(y) // [0 0 0]
y[0] = 2
y[1] = 4
y[2] = 6
fmt.Println(y) // [2 4 6]
}
در صورتی که پیش از ایجاد اسلایس, مقادیری که قرار است درون اسلایس قرار بگیرند را میدانید, می توانید از لیترال اسلایس (slice literal) استفاده کنید:
package main
import "fmt"
func main() {
y := []int{2, 4, 6}
fmt.Println(y) // [2 4 6]
}
پس تا اینجا یاد گرفتیم ۳ روش برای ایجاد یک اسلایس وجود دارد:
- تعریف اسلایس و سپس تخصیص حافظه با استفاده از دستور
make
- ایجاد اسلایس با استفاده از
:=
و دستورmake
- استفاده از لیترال اسلایس
ساختار اسلایس در حافظه
اسلایس یک دیتا استراکچر است که از سه بخش تشکیل شده است
- یک اشاره گر (pointer) به یک آرایه
- یک عدد صحیح که طول اسلایس را نشان میدهد (len)
- یک عدد صحیح که ظرفیت اسلایس را نشان میدهد (cap)
هنگامی که یک اسلایس را تعریف میکنیم (declare) مقدار صفر آرایه (zero value) برابر با nil
است. این بدین معناست که اشاره گر در این اسلایس به چیزی اشاره نمیکند و مقادیر len و cap برابر با صفر هستند.
هنگامی که از تابع make
استفاده میکنیم, این تابع ابتدا یک اسلایس ایجاد میکند, سپس یک آرایه با اندازه (cap) داده شده ایجاد میکند و اشاره گر اسلایس را طوری تنظیم میکند که به اولین عنصر آن آرایه اشاره کند, سپس مقادیر len و cap را تنظیم میکند و در اخر اسلایس ایجاد شده را به عنوان نتیجه برمیگرداند
تابع make
سه پارامتر به عنوان ورودی قبول میکند. دو پارامتر اول اجباری و پارامتر سوم آپشنال (دلخواه یا optional) است. پارامتر اول نمایانگر نوع دیتا, پارامتر دوم len و پارامتر سوم cap را مشخص میکند.
طول و ظرفیت یک اسلایس را به ترتیب با توابع len و cap میتوان تشخیص داد:
package main
import "fmt"
func main() {
var emails []string
fmt.Println(len(emails), cap(emails)) // 0 0
emails = make([]string, 10)
fmt.Println(len(emails), cap(emails)) // 10 10
emails = make([]string, 2, 5)
fmt.Println(len(emails), cap(emails)) // 2 5
}
منظور از cap و len چیست؟
اگر طول (len) اسلایس برابر با ۵ باشد یعنی به اندیس های ۰ تا ۴ دسترسی خواهیم داشت (از ۰ تا ۴ در مجموع ۵ مقدار قرار خواهد گرفت):
package main
import "fmt"
func main() {
emails := make([]float64, 2, 5)
fmt.Println(len(emails), cap(emails)) // 2 5
fmt.Println(emails) // 0 0
emails[0] = 2
emails[1] = 4
fmt.Println(emails) // 2 4
}
در صورتی که بخواهیم عناصر خارج از محدوده len را بخوانیم یا مقدار دهی کنیم, با خطا روبرو خواهیم شد:
package main
import "fmt"
func main() {
emails := make([]float64, 2, 5)
fmt.Println(len(emails), cap(emails)) // 2 5
fmt.Println(emails) // 0 0
emails[0] = 2
emails[1] = 4
emails[2] = 5 // panic: runtime error: index out of range [2] with length 2
}
برای افزودن یک یا چند مقدار به انتهای اسلایس میتوان از تابع append
استفاده کرد.
package main
import "fmt"
func main() {
numbers := make([]float64, 2, 5)
fmt.Println(len(numbers), cap(numbers)) // 2 5
fmt.Println(numbers) // 0 0
numbers[0] = 2
numbers[1] = 4
numbers = append(numbers, 6, 8)
fmt.Println(numbers) // 2 4 6 8
fmt.Printf("len=%d cap=%d", len(numbers), cap(numbers)) // len=4 cap=5
}
تابع append
مقادیر داده شده را به اِنتهای آرایه اضافه کرده و طول آرایه را به اندازه تعدادِ مقادیر افزایش میدهد. در صورتی که طول اسلایس (len) بزرگتر از ظرفیت (cap) آن شود, آرایه ای که اشاره گرِ اسلایس به آن اشاره میکند ظرفیت ذخیره کردن مقادیر جدید را نخواهد داشت و تابع append
در این مواقع به صورت زیر عمل میکند:
- یک آرایه جدید با ظرفیت دوبرابر آرایه قبلی ایجاد میکند.
- مقادیر آرایه قبلی را در آرایه جدید کپی میکند.
- اشاره گر اسلایس را طوری تنظیم میکند که به آرایه جدید اشاره کند.
- مقادیر len و cap را در اسلایس تنظیم میکند.
کپی کردن مقادیر
برای کپی کردن مقادیر از یک اسلایس به اسلایس دیگر میتوان از تابع copy
استفاده کرد.
package main
import "log"
func main() {
x := []string{"John", "Michael", "Jane", "Kyle", "Sara"}
y := make([]string, 3)
number := copy(y, x)
log.Println(y) // [John Michael Jane]
log.Println(number) // 3
}
تابع copy
به اندازه طول پارامتر اول, مقادیر را از پارامتر دوم به درون پارامتر اول کپی میکند و یک عدد که برابر با تعداد آیتمِ کپی شده است را برمیگرداند.
اسلایس کردن
شما میتوانید یک آرایه یا اسلایس را دوباره به قسمت های کوچکتر اسلایس کنید, برای اینکار میتوانید از دستور [start:end]
استفاده کنید. اسلایس ایجاد شده شامل تمامی آیتم ها از ایندکس start تا end خواهد بود اما خود end را شامل نمی شود.
package main
import "log"
func main() {
x := [...]int{1, 1, 2, 3, 5, 8}
y := x[0:3]
log.Println(y) // [1 1 2]
log.Println(len(y), cap(y)) // 3 5
y[0] = 100
log.Println(x[0], y[0]) // 100 100
}
با اسلایس کردن یک آرایه یا اسلایس, پوینترِ اسلایسِ جدید به آرایه قبلی یا جایی که پونترِ اسلایس اصلی اشاره میکند اشاره خواهد کرد. بدین ترتیب اگر مقداری را در اسلایسِ جدید ایجاد شده تغییر دهیم در متغیر اصلی هم تغییر خواهد کرد.
- طول یا len اسلایس جدید ایجاد شده برابر با تفاضلِ اندیسِ شروع و اندیسِ پایان خواهد بود.
- ظرفیت یا cap اسلایس جدید برابر با تفاضلِ اندیسِ شروع و ظرفیتِ آرایه یا اسلایس اصلی است.
میتوان اندیس شروع و پایان را مشخص نکرد. در این صورت اندیس شروع برابر با 0
و اندیس پایان برابر با طول آرایه یا اسلایس اصلی در نظر گرفته میشود.
package main
import "log"
func main() {
a := []int{1, 1, 2, 3, 5, 8}
log.Println(len(a), cap(a)) // 6 6
b := a[:]
log.Println(b) // [1 1 2 3 5 8]
log.Println(len(b), cap(b)) // 6 6
c := a[3:]
log.Println(c) // [3 5 8]
log.Println(len(c), cap(c)) // 3 3
d := b[:2]
log.Println(d) // 1 1
log.Println(len(d), cap(d)) // 2 6
e := a[5:6]
log.Println(e) // [8]
log.Println(len(e), cap(e)) // 1 1
}
قسمت قبلی: نوع داده متنی (string) | گولنگ به زبان ساده
قسمت بعدی: حلقه ها | گولنگ به زبان ساده