একটি ফাংশন ব্যবহার করে আপনি কোডকে একটি পুনর্ব্যবহারযোগ্য ইউনিটে গুছিয়ে ফেলতে পারেন।
এটি তৈরি হয় func কিওয়ার্ড, ফাংশনের নাম এবং বন্ধনীর ভেতরে কমা দিয়ে আলাদা করা শূন্য বা তার বেশি প্যারামিটার ও টাইপের একটি তালিকা দিয়ে।
সব প্যারামিটারের টাইপ স্পষ্টভাবে উল্লেখ করতে হয়; প্যারামিটারের জন্য কোনো টাইপ ইনফারেন্স নেই। প্যারামিটারের কোনো ডিফল্ট মান নেই, তাই ফাংশনের সব প্যারামিটারই আবশ্যক।
import "fmt"
// No parameters
func PrintHello() {
fmt.Println("Hello")
}
// One parameter
func PrintHelloName(name string) {
fmt.Println("Hello " + name)
}
একই টাইপের প্যারামিটারগুলো একসাথে ঘোষণা করা যায়, আর শেষে একটি মাত্র টাইপ উল্লেখ করতে হয়।
import "fmt"
func PrintGreetingName(greeting, name string) {
fmt.Println(greeting + " " + name)
}
চলুন দ্রুত দুটি পরিভাষা দেখে নিই যেগুলো প্রায়ই একসাথে গুলিয়ে ফেলা হয়: parameters এবং arguments।
ফাংশন প্যারামিটার হলো ফাংশনের সিগনেচারে ডিফাইন করা নামগুলো, যেমন উপরের PrintGreetingName ফাংশনের greeting ও name।
ফাংশন আর্গুমেন্ট হলো ফাংশন কল করার সময় ফাংশন প্যারামিটারে পাঠানো নির্দিষ্ট মানগুলো।
যেমন, নিচের উদাহরণে "Hello" আর "Katrina" হলো greeting ও name প্যারামিটারে পাঠানো আর্গুমেন্ট:
PrintGreetingName("Hello", "Katrina")
ফাংশন প্যারামিটারের পরে শূন্য বা তার বেশি রিটার্ন ভ্যালু আসে, যেগুলোর টাইপও স্পষ্টভাবে উল্লেখ করতে হয়।
একটি মাত্র রিটার্ন ভ্যালু হলে সেটি খোলা অবস্থায় রাখা হয়, আর একাধিক রিটার্ন ভ্যালু হলে সেগুলো বন্ধনী দিয়ে ঘিরে দেওয়া হয়।
return কিওয়ার্ড ব্যবহার করে ফাংশন থেকে কল করা কোডে মান রিটার্ন করা হয়।
একটি ফাংশনে একাধিক return স্টেটমেন্ট থাকতে পারে।
ফাংশনের এক্সিকিউশন ওই return স্টেটমেন্টগুলোর যেকোনো একটিতে পৌঁছালেই শেষ হয়ে যায়।
কোনো ফাংশন থেকে যদি একাধিক মান রিটার্ন করতে হয়, তবে সেগুলোকে কমা দিয়ে আলাদা করা হয়।
একাধিক রিটার্ন ভ্যালু-এর প্রচলিত ব্যবহার সম্পর্কে আরও তথ্য লিংক করা কনসেপ্টটিতে পাওয়া যাবে।
func Hello(name string) string {
return "Hello " + name
}
func HelloAndGoodbye(name string) (string, string) {
return "Hello " + name, "Goodbye " + name
}
ফাংশন কল করতে হলে ফাংশনের নাম লিখতে হয় এবং বন্ধনীর ভেতরে ফাংশনের প্রতিটি প্যারামিটারের জন্য আর্গুমেন্ট পাঠাতে হয়।
import "fmt"
// No parameters, no return value
func PrintHello() {
fmt.Println("Hello")
}
// Called like this:
PrintHello()
// One parameter, one return value
func Hello(name string) string {
return "Hello " + name
}
// Called like this:
greeting := Hello("Dave")
// Multiple parameters, multiple return values
func SumAndMultiply(a, b int) (int, int) {
return a+b, a*b
}
// Called like this:
aplusb, atimesb := SumAndMultiply(a, b)
প্যারামিটারের মতো রিটার্ন ভ্যালুও চাইলে নাম দেওয়া যায়।
নামযুক্ত রিটার্ন ভ্যালু ব্যবহার করলে, আর্গুমেন্ট ছাড়া একটি return স্টেটমেন্ট ওই মানগুলোই রিটার্ন করবে।
এটাকেই 'নেকেড' রিটার্ন বলা হয়।
func SumAndMultiplyThenMinus(a, b, c int) (sum, mult int) {
sum, mult = a+b, a*b
sum -= c
mult -= c
return
}
ভ্যালু দিয়ে পাস আর রেফারেন্স দিয়ে পাসের ধারণাটি স্পষ্ট করাও জরুরি।
প্রথমে চলুন ভ্যালু দিয়ে পাসের বিষয়টি স্পষ্ট করি।
নিচের উদাহরণে, আমরা যখন val ভ্যারিয়েবলটি MultiplyByTwo ফাংশনে পাঠাই, তখন আসলে val-এর একটি কপি পাঠাই।
এ কারণে newVal-এ আপডেট হওয়া মান 4 থাকে, কিন্তু মূল ভ্যারিয়েবল val তখনও 2 থাকে।
পর্দার আড়ালে Go আসলে মূল মানের একটি কপি তৈরি করে, যাতে ফাংশন শুধু এই কপিটিকেই, অর্থাৎ v-কে, পরিবর্তন করে।
val := 2
func MultiplyByTwo(v int) int {
v = v * 2
return v
}
newVal := MultiplyByTwo(val)
// newval is 4, val is still 2 because only a copy of its value was passed into the function
কঠোরভাবে বললে, Go-তে সব আর্গুমেন্টই ভ্যালু দিয়ে পাস করা হয়, অর্থাৎ ফাংশনে দেওয়া মান বা ডেটার একটি কপি তৈরি করা হয়।
কিন্তু ফাংশনে পাঠানো ডেটার কপি তৈরি করতে না চাইলে এবং ফাংশনের ভেতরে সেই ডেটা পরিবর্তন করতে চাইলে, আপনার আর্গুমেন্ট হিসেবে pointers ব্যবহার করা উচিত, অর্থাৎ রেফারেন্স দিয়ে পাস।
রেফারেন্স দিয়ে পাস করার জন্য আমরা pointer ব্যবহার করি।
ফাংশনে pointer আর্গুমেন্ট পাঠানোর মাধ্যমে আমরা ডেটার কেবল একটি কপি নিয়ে কাজ না করে ফাংশনে পাঠানো মূল ডেটাটিই পরিবর্তন করতে পারি।
আপাতত এটা জানাই যথেষ্ট যে ফাংশন সিগনেচারে টাইপের সামনের * দেখে পয়েন্টার টাইপ চেনা যায়।
func HandlePointers(x, y *int) {
// Some logic to handle integer values referenced by pointers x and y
}
pointer-এর ধারণাটি যদি বিভ্রান্তিকর মনে হয়, তবে চিন্তার কিছু নেই।
সিলেবাসে পরে একটি আলাদা অংশ আছে যা আপনাকে পয়েন্টার আয়ত্ত করতে সাহায্য করবে।
মনে রাখবেন, slices ও maps হলো উপরে বলা নিয়মের ব্যতিক্রম।
আমরা যখন কোনো ফাংশনে আর্গুমেন্ট হিসেবে একটি slice বা map পাঠাই, তখন টাইপে স্পষ্ট কোনো * না থাকলেও সেগুলোকে পয়েন্টার টাইপ হিসেবে ধরা হয়।
এর মানে হলো, আমরা যদি কোনো ফাংশনে একটি স্লাইস বা ম্যাপ পাঠিয়ে তার মূল ডেটা পরিবর্তন করি, তবে সেই পরিবর্তন মূল স্লাইস বা ম্যাপে প্রতিফলিত হবে।
Go-তে ফাংশনকে ফার্স্ট ক্লাস সিটিজেন ধরা হয়, যা তাদের খুবই শক্তিশালী করে তোলে। ফাংশন নিয়ে আরও অনেক ধারণা আছে, যেমন মেথড আর অ্যানোনিমাস ফাংশন, যেগুলোর সাথে আপনি আপনার যাত্রাপথে পরে পরিচিত হবেন।