اسلایس در Go شبیه لیست یا آرایه در زبانهای دیگر است. اسلایس چندین عنصر از یک نوع مشخص (یا رابط) را نگه میدارد.
اسلایس در Go بر پایهی آرایههاست. آرایه اندازهی ثابتی دارد. اسلایس اما یک نمای پویا و انعطافپذیر از عناصر یک آرایه است.
اسلایس بهصورت []T نوشته میشود که در آن T نوع عناصر اسلایس است:
var empty []int // an empty slice
withData := []int{0,1,2,3,4,5} // a slice pre-filled with some data
میتوانید یک عنصر را در اندیس مشخصی که از صفر شروع میشود، با استفاده از نماد کروشه دریافت یا تنظیم کنید:
withData[1] = 5
x := withData[1] // x is now 5
میتوانید با گرفتن بازهای از عناصر، اسلایس جدیدی از یک اسلایس موجود بسازید. باز هم با استفاده از نماد کروشه، اما این بار هم اندیس شروع (شامل) و هم اندیس پایان (غیرشامل) را مشخص میکنید. اگر اندیس شروع را مشخص نکنید، بهطور پیشفرض ۰ میشود. اگر اندیس پایان را مشخص نکنید، بهطور پیشفرض طول اسلایس میشود.
newSlice := withData[2:4]
// => []int{2,3}
newSlice := withData[:2]
// => []int{0,1}
newSlice := withData[2:]
// => []int{2,3,4,5}
newSlice := withData[:]
// => []int{0,1,2,3,4,5}
میتوانید با استفاده از تابع append عناصری به اسلایس اضافه کنید.
در زیر 4 و 2 را به اسلایس a اضافه میکنیم.
a := []int{1, 3}
a = append(a, 4, 2)
// => []int{1,3,4,2}
append همیشه یک اسلایس جدید برمیگرداند، و وقتی فقط میخواهیم عناصری به یک اسلایس موجود اضافه کنیم، معمول است که آن را دوباره به متغیر اسلایسی که بهعنوان آرگومان اول پاس میدهیم اختصاص دهیم، همانطور که در بالا انجام دادیم.
از append میتوان برای ادغام دو اسلایس هم استفاده کرد:
nextSlice := []int{100,101,102}
newSlice := append(withData, nextSlice...)
// => []int{0,1,2,3,4,5,100,101,102}
معمولاً توابع در Go فقط تعداد ثابتی آرگومان میپذیرند. با این حال، نوشتن تابع متغیر در Go هم ممکن است.
تابع متغیر تابعی است که تعداد متغیری از آرگومانها را میپذیرد.
اگر نوع آخرین پارامتر در تعریف تابع با سهنقطه ... شروع شود، آنگاه تابع میتواند هر تعداد آرگومان برای آن پارامتر بپذیرد.
func find(a int, b ...int) {
// ...
}
در تابع بالا، پارامتر b متغیر است و میتوانیم ۰ یا بیشتر آرگومان به b پاس دهیم.
find(5, 6)
find(5, 6, 7)
find(5)
پارامتر متغیر باید آخرین پارامتر تابع باشد.
روش کار توابع متغیر این است که تعداد متغیر آرگومانها را به اسلایسی از نوع پارامتر متغیر تبدیل میکنند.
در اینجا نمونهای از پیادهسازی یک تابع متغیر را میبینید.
func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
return
}
}
fmt.Println(num, "not found in ", nums)
}
func main() {
find(89, 90, 91, 95)
// =>
// type of nums is []int
// 89 not found in [90 91 95]
find(45, 56, 67, 45, 90, 109)
// =>
// type of nums is []int
// 45 found at index 2 in [56 67 45 90 109]
find(87)
// =>
// type of nums is []int
// 87 not found in []
}
در خط find(89, 90, 91, 95) از برنامهی بالا، تعداد متغیر آرگومانهای تابع find برابر با 90، 91 و 95 است.
تابع find انتظار یک پارامتر متغیر از نوع int را بعد از num دارد.
از این رو این سه آرگومان توسط کامپایلر به یک اسلایس از نوع int یعنی []int{90, 91, 95} تبدیل میشوند و سپس بهعنوان nums به تابع find پاس داده میشوند.
گاهی از قبل یک اسلایس دارید و میخواهید آن را به یک تابع متغیر پاس دهید.
این کار با پاس دادن اسلایس و سپس ... انجام میشود.
این به کامپایلر میگوید که اسلایس را همانطور که هست درون تابع متغیر استفاده کند.
در این حالت، مرحلهای که در بالا توضیح داده شد و در آن یک اسلایس ساخته میشود، بهسادگی حذف میشود.
list := []int{1, 2, 3}
find(1, list...) // "find" defined as shown above
الیس که میخواهد شعبدهباز شود، باید اصول اولیه را تمرین کند. او یک دسته کارت دارد که میخواهد با آن کار کند.
برای اینکه کار کمی سادهتر شود، او فقط از کارتهای ۱ تا ۱۰ استفاده میکند.
وقتی الیس با کارتهایش تمرین میکند، دوست دارد کارش را با سه کارت محبوبش از دسته شروع کند: ۲، ۶ و ۹.
تابعی به اسم FavoriteCards بنویسید که یک slice با همان کارتها به همان ترتیب برمیگرداند.
cards := FavoriteCards()
fmt.Println(cards)
// Output: [2 6 9]
کارت واقع در موقعیت index را از دستهی دادهشده برگردانید.
card := GetItem([]int{1, 2, 4, 1}, 2) // card == 4
اگر اندیس «خارج از محدوده» باشد (یعنی منفی باشد یا بعد از انتهای دسته)، میخواهیم -1 را برگردانیم:
card := GetItem([]int{1, 2, 4, 1}, 10) // card == -1
بر اساس قرارداد در Go، بهجای برگرداندن یک مقدار خارج از محدوده، یک خطا برگردانده میشود.
اینجا وقتی یک عدد صحیح مثبت انتظار میرود، مقدار خارج از محدوده -1 است.
هنگام برگرداندن خطا، مرسوم است که zero value را همراه با خطا برگردانید.
برگرداندن خطا همراه با مقدار بازگشتی مناسب در تمرین آینده بررسی میشود.
کارت واقع در موقعیت index را با کارت جدیدی که داده شده تعویض کنید و دستهی تنظیمشده را برگردانید.
توجه کنید که این کار slice ورودی را تغییر میدهد که رفتار مورد انتظار است.
index := 2
newCard := 6
cards := SetItem([]int{1, 2, 4, 1}, index, newCard)
fmt.Println(cards)
// Output: [1 2 6 1]
اگر اندیس خارج از محدوده باشد (یعنی منفی باشد یا بعد از انتهای دسته)، میخواهیم کارت جدید را به انتهای دسته اضافه کنیم:
index := -1
newCard := 6
cards := SetItem([]int{1, 2, 4, 1}, index, newCard)
fmt.Println(cards)
// Output: [1 2 4 1 6]
کارت یا کارتهای مشخصشده در پارامتر value را به بالای دسته اضافه کنید.
slice := []int{3, 2, 6, 4, 8}
cards := PrependItems(slice, 5, 1)
fmt.Println(cards)
// Output: [5 1 3 2 6 4 8]
اگر برای پارامتر value هیچ آرگومانی داده نشود، نتیجه برابر با slice اصلی است.
slice := []int{3, 2, 6, 4, 8}
cards := PrependItems(slice)
fmt.Println(cards)
// Output: [3 2 6 4 8]
کارت واقع در موقعیت index را از دسته حذف کنید و دسته را برگردانید.
توجه کنید که این کار ممکن است slice ورودی را تغییر دهد که اشکالی ندارد.
cards := RemoveItem([]int{3, 2, 6, 4, 8}, 2)
fmt.Println(cards)
// Output: [3 2 4 8]
اگر اندیس خارج از محدوده باشد (یعنی منفی باشد یا بعد از انتهای دسته)، میخواهیم دسته بدون تغییر بماند:
cards := RemoveItem([]int{3, 2, 6, 4, 8}, 11)
fmt.Println(cards)
// Output: [3 2 6 4 8]