একটি ফাংশন আপনাকে কোডকে একটি পুনর্ব্যবহারযোগ্য এককে গুছিয়ে রাখতে দেয়।
এটি তৈরি হয় func কিওয়ার্ড, ফাংশনের নাম, এবং প্রথম বন্ধনীর ভেতরে কমা দিয়ে আলাদা করা শূন্য বা তার বেশি প্যারামিটার ও টাইপের তালিকা নিয়ে।
সব প্যারামিটারের টাইপ স্পষ্টভাবে উল্লেখ করতে হয়; প্যারামিটারের জন্য কোনো টাইপ ইনফারেন্স নেই। প্যারামিটারের কোনো ডিফল্ট মানও নেই, তাই ফাংশনের সব প্যারামিটারই আবশ্যক।
import "fmt"
// No parameters
func PrintHello() {
fmt.Println("Hello")
}
// Two parameters
func PrintGreetingName(greeting string, name string) {
fmt.Println(greeting + " " + 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
}
ভ্যালু দিয়ে পাস আর রেফারেন্স দিয়ে পাসের ধারণাটা স্পষ্ট করাও জরুরি।
প্রথমে চলুন ভ্যালু দিয়ে পাস স্পষ্ট করে নিই।
নিচের উদাহরণে, আমরা যখন MultiplyByTwo ফাংশনে val ভ্যারিয়েবলটি পাঠাই, তখন আসলে 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 পাঠালে সেগুলোকে পয়েন্টার টাইপ হিসেবে ধরা হয়, যদিও টাইপে স্পষ্ট কোনো * থাকে না।
এর মানে হলো, আমরা যদি কোনো ফাংশনে একটি স্লাইস বা ম্যাপ পাঠিয়ে তার মূল ডেটা পরিবর্তন করি,
তাহলে পরিবর্তনগুলো মূল স্লাইস বা ম্যাপে প্রতিফলিত হবে।
এই অনুশীলনীতে আপনি আপনার প্রিয় রান্নার বইয়ের সেই চমৎকার লাসাগনা প্রস্তুত ও রান্নার সাথে সম্পর্কিত আরও কিছু কোড লিখবেন।
আপনার সামনে চারটি কাজ আছে। প্রথমটি রান্নার সাথে সম্পর্কিত, বাকি তিনটি নিখুঁত প্রস্তুতি নিয়ে।
আপনি যে পরের লাসাগনাটি তৈরি করবেন, তার জন্য যথেষ্ট সময় রাখতে চান, যাতে রান্নাটি উপভোগ করতে পারেন। আপনার লাসাগনায় কোন কোন স্তর থাকবে তা আপনি আগেই ঠিক করে ফেলেছেন। এখন সেই অনুযায়ী প্রস্তুতিতে কত সময় লাগবে তা অনুমান করতে চান।
[]string হিসেবে স্তরগুলোর একটি স্লাইস এবং int হিসেবে মিনিটে প্রতি স্তরের গড় প্রস্তুতির সময় নেয় এমন একটি PreparationTime ফাংশন ইমপ্লিমেন্ট করুন।
ফাংশনটি স্তরের সংখ্যার ভিত্তিতে মোট প্রস্তুতির সময়ের অনুমান int হিসেবে রিটার্ন করবে।
Go-তে ফাংশনের জন্য ডিফল্ট মান বলে কিছু নেই।
যদি গড় প্রস্তুতির সময় 0 হিসেবে পাঠানো হয় (int-এর ডিফল্ট প্রাথমিক মান), তাহলে ডিফল্ট মান 2 ব্যবহার করতে হবে।
layers := []string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"}
PreparationTime(layers, 3)
// => 18
PreparationTime(layers, 0)
// => 12
সময় আলাদা রাখার পাশাপাশি, আপনার স্বপ্নের লাসাগনা রান্নার জন্য যথেষ্ট সস ও নুডলস আছে কি না তাও নিশ্চিত করতে চান আপনি। আপনার লাসাগনার প্রতিটি নুডলসের স্তরের জন্য ৫০ গ্রাম নুডলস লাগবে। আপনার লাসাগনার প্রতিটি সসের স্তরের জন্য ০.২ লিটার সস লাগবে।
Quantities ফাংশনটি ডিফাইন করুন, যা প্যারামিটার হিসেবে []string টাইপের স্তরের একটি স্লাইস নেয়।
এরপর ফাংশনটি আপনার খাবার তৈরি করতে প্রয়োজনীয় নুডলস ও সসের পরিমাণ নির্ধারণ করবে।
ফলাফল দুটি মান হিসেবে রিটার্ন করতে হবে: noodles একটি int হিসেবে এবং sauce একটি float64 হিসেবে।
Quantities([]string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})
// => 100, 0.4
কিছুদিন আগে আপনি এক বন্ধুর বাড়িতে গিয়েছিলেন এবং সেখানে লাসাগনা খেয়েছিলেন। খাবারটি ছিল অসাধারণ, তাতে বিশেষ কিছু একটা ছিল। বন্ধুটি আপনাকে উপকরণের তালিকা পাঠিয়েছিলেন এবং বলেছিলেন, তালিকার শেষ উপাদানটিই সেই "গোপন উপাদান", যা খাবারটিকে এত বিশেষ করে তুলেছিল। এখন আপনি সেই গোপন উপাদানটি নিজের রেসিপিতেও যোগ করতে চান।
AddSecretIngredient নামের একটি ফাংশন লিখুন, যা প্যারামিটার হিসেবে []string টাইপের উপকরণের দুটি স্লাইস নেয়।
প্রথম প্যারামিটারটি আপনার বন্ধুর পাঠানো তালিকা, দ্বিতীয়টি আপনার নিজের রেসিপির উপকরণের তালিকা।
আপনার উপকরণের তালিকার শেষ এলিমেন্টটি সবসময় "?"।
ফাংশনটি সেটিকে আপনার বন্ধুর তালিকার শেষ উপাদান দিয়ে বদলে দেবে।
লক্ষ্য করুন: AddSecretIngredient কিছুই রিটার্ন করে না, আপনাকে সরাসরি আপনার উপকরণের তালিকাটি পরিবর্তন করতে হবে।
আপনার বন্ধুর উপকরণের তালিকাটি পরিবর্তন করা উচিত নয়।
এছাড়া, যেহেতু slice ফাংশনে পয়েন্টার হিসেবে পাঠানো হয়, সেহেতু AddSecretIngredient-এ পাঠানো দুটি []string আর্গুমেন্ট সরাসরি পরিবর্তিত হবে।
friendsList := []string{"noodles", "sauce", "mozzarella", "kampot pepper"}
myList := []string{"noodles", "meat", "sauce", "mozzarella","?"}
AddSecretIngredient(friendsList, myList)
// myList => []string{"noodles", "meat", "sauce", "mozzarella", "kampot pepper"}
আপনার রান্নার বইয়ে দেওয়া পরিমাণ দিয়ে মাত্র দুই পোরশনের লাসাগনা হয়। যেহেতু পরেরবার আপনি আরও মানুষের জন্য রান্না করতে চান, তাই বিভিন্ন সংখ্যক পোরশনের জন্য পরিমাণ হিসাব করতে চান।
দুইটি প্যারামিটার নেয় এমন একটি ScaleRecipe ফাংশন ইমপ্লিমেন্ট করুন।
float64 পরিমাণের একটি স্লাইস।ফাংশনটি চাওয়া সংখ্যক পোরশনের জন্য প্রয়োজনীয় পরিমাণের একটি float64 স্লাইস রিটার্ন করবে।
তবে আপনি মূল রেসিপিটি অপরিবর্তিত রাখতে চান।
এর মানে এই ফাংশনে quantities আর্গুমেন্টটি পরিবর্তন করা উচিত নয়।
quantities := []float64{ 1.2, 3.6, 10.5 }
scaledQuantities := ScaleRecipe(quantities, 4)
// => []float64{ 2.4, 7.2, 21 }
Exercism-এ সাইন আপ করুন, Go ট্র্যাকের 34টি কনসেপ্ট166টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।