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
এলিমেন্টের একটি রেঞ্জ নিয়ে বিদ্যমান স্লাইস থেকে নতুন স্লাইস তৈরি করা যায়। আবারও তৃতীয় বন্ধনী নোটেশন, তবে এবার শুরুর (অন্তর্ভুক্ত) ও শেষের (অন্তর্ভুক্ত নয়) ইনডেক্স দুটোই উল্লেখ করা হয়। শুরুর ইনডেক্স না দিলে সেটি ডিফল্টভাবে 0 হয়। শেষের ইনডেক্স না দিলে সেটি ডিফল্টভাবে স্লাইসের দৈর্ঘ্য হয়।
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 ফাংশন ব্যবহার করে স্লাইসে এলিমেন্ট যোগ করা যায়। নিচে আমরা a স্লাইসে 4 ও 2 যোগ করছি।
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-তে 0 বা তার বেশি আর্গুমেন্ট পাস করতে পারি।
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 ফাংশনটি num-এর পরে একটি ভ্যারিয়াডিক int প্যারামিটার আশা করে। তাই কম্পাইলার এই তিনটি আর্গুমেন্টকে int টাইপের একটি স্লাইস []int{90, 91, 95} হিসেবে রূপান্তর করবে, তারপর সেটি nums হিসেবে find ফাংশনে পাস করা হবে।
মাঝে মাঝে আপনার কাছে আগে থেকেই একটি স্লাইস থাকে এবং সেটি একটি ভ্যারিয়াডিক ফাংশনে পাস করতে চান। স্লাইসটির পরে ... দিয়ে পাস করলেই এটি করা যায়। এটি কম্পাইলারকে বলে দেবে যেন ভ্যারিয়াডিক ফাংশনের ভেতরে স্লাইসটি যেমন আছে তেমনই ব্যবহার করা হয়। উপরে বর্ণিত যে ধাপে একটি স্লাইস তৈরি হয়, সেটি এই ক্ষেত্রে বাদ পড়বে।
list := []int{1, 2, 3}
find(1, list...) // "find" defined as shown above
হাতে-কলমে জাদুকর হওয়ার পথে এলিসের কিছু মৌলিক বিষয় অনুশীলন করা দরকার। তার কাছে কার্ডের একটি স্ট্যাক আছে, যেটি নিয়ে সে নানা কাজ করতে চায়।
কাজটা একটু সহজ করার জন্য সে শুধু ১ থেকে ১০ নম্বর কার্ডগুলোই ব্যবহার করে।
কার্ড নিয়ে অনুশীলন করার সময় এলিস ডেকের তার পছন্দের তিনটি কার্ড দিয়ে শুরু করতে ভালোবাসে: ২, ৬ আর ৯।
এমন একটি ফাংশন FavoriteCards লিখুন যা ওই কার্ডগুলো একই ক্রমে নিয়ে একটি স্লাইস রিটার্ন করে।
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 অবস্থানের কার্ডটিকে প্রদত্ত নতুন কার্ডের সাথে বদলে দিন এবং সমন্বিত স্ট্যাকটি রিটার্ন করুন।
মনে রাখবেন, এটি ইনপুট স্লাইসটিকে পরিবর্তন করবে, আর এটাই প্রত্যাশিত আচরণ।
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 := []int{3, 2, 6, 4, 8}
cards := PrependItems(slice)
fmt.Println(cards)
// Output: [3 2 6 4 8]
স্ট্যাক থেকে index অবস্থানের কার্ডটি সরিয়ে স্ট্যাকটি রিটার্ন করুন।
মনে রাখবেন, এটি ইনপুট স্লাইসটি পরিবর্তন করতে পারে, আর তাতে কোনো সমস্যা নেই।
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]
Exercism-এ সাইন আপ করুন, Go ট্র্যাকের 34টি কনসেপ্ট165টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।