ফাংশন ও এর ব্যবহার

functionফাংশন নির্দিষ্ট কোড ব্লককে সম্পুর্ন অ্যাপ্লিকেশনে রিইউজ করার সুবিধা দেয়। ধরি, আমি খুব ভোজন রসিক এবং নিজেই নতুন নতুন রান্না করে খেতেই বেশি পছন্দ করি। একারনে আমি একেকবার একেক জনের কাছ থেকে বিভিন্ন খাবারের রেসিপি জেনে নেই। এবার ধরি, আমি একজন নির্দিষ্ট বাবুর্চি চিনে রাখলাম যার কাজ হবে আমাকে রেসিপি জানানো। সে যেখানেই থাকুক না কেন আমি তাকে ফোন করে খাবারের নাম বলব এবং সে আমাকে রেসিপিটা জানাবে। এখন কিন্তু একেক বার একেক লোকের সাহায্য লাগবে না আমার। একজনই থাকবে, আমার যখন দরকার পরবে, তখন সেই একজনকেই আমি ব্যবহার করব। এই নির্দিষ্ট একজন লোকই প্রোগ্রামিং এর ভাষায় ফাংশন। আমরা আমাদের সাধারণ এবং সবসময় যেগুলো লাগে সেই কাজ গুলোকে কিছু স্বাধীন ভাগে ভাগ করব এবং সেই একেক ভাগকে আলাদা আলাদা ফাংশন হিসেবে ডিফাইন (কার্যকলাপ নির্ধারণ করে দেয়া) করব। তাহলে একই কাজের জন্য বার বার কোড লিখতে হবে না। শুধু ওই ফাংশনকে ডাকলেই (ফাংশন কল) হবে।

ফাংশনের ব্যাসিক সিন্ট্যাক্সঃ একটি ফাংশনের মোট চারটি অংশ থাকে-

  • রিটার্ন ভ্যালু (এই ফাংশনটি কাজ শেষে যদি কোন কিছু ফেরত/ফলাফল দেয় তাহলে সেটির ডাটাটাইপ কি হবে)
  • নাম (ফাংশনটির একটি নির্দিষ্ট নাম থাকবে যা দিয়ে তাকে ডাকা হবে বার বার)
  • প্যারামিটারস (ফাংশনটির কাজ সম্পুর্ন করতে যদি তথ্যের দরকার পরে তাহলে সেই তথ্য গুলো)
  • কোড ব্লক (এই অংশে ফাংশনটা যে কাজ করবে তার প্রোগ্রাম্যাটিকাল কোড/লজিক থাকে)

উদাহরন স্বরুপ নিচের কোডটুকু দেখা যাক,

#import 

void billKotoHolo()
{
    NSLog(@"স্যার আপনার বিল হয়েছে ১২০০ টাকা");
}


int main(int argc, const char * argv[])
{

    @autoreleasepool {
        billKotoHolo();
    }
    return 0;
}

এখানে দেখা যাচ্ছে billKotoHolo() একটা ফাংশন। মেইন main() ফাংশন এর মধ্যে থেকে একে কল করা হয়েছে। এই ফাংশনটি মেইন ফাংশনকে কোন কিছু রিটার্ন করবে না তাই এর রিটার্ন টাইপ void। এই ফাংশনের কার্যকলাপ বলতে গেলে সিমপ্লি কনসোলে একটা ম্যাসেজ প্রিন্ট করা আর এই কাজটুকু করতে এর কোন প্যারামিটার দরকার নাই। তাই ডেফিনেশন এর সময় () এর মধ্যে কিছু নাই। {} এর ভিতরের অংশই কোডব্লক যেখানে বলা আছে এই ফাংশনের কাজ কি। আবার, কল করার সময়ও কোন প্যারামিটার পাঠানো হয়নি তাই () ব্র্যাকেটের ভিতরের অংশ ফাকা। অর্থাৎ কোন প্যারামিটার পাস করা হচ্ছে না।

ফাংশনে, পয়েন্টার রেফারেন্সকেও রিটার্ন ভ্যালু এবং প্যারামিটার হিসেবে ব্যবহার করা যায়। অর্থাৎ ফাংশন গুলো খুব সহজেই Objective-C এর অবজেক্ট গুলোর সাথে ব্যবহৃত হতে পারে। কারন, আগেই বলা হয়েছে যে, “Objective-C তে সব ধরনের অবজেক্টকে পয়েন্টার হিসেবে প্রকাশ করা হয়/যায়।” এখন নিচের উদাহরণটা খেয়াল করুন,

#import 

int getCount(NSArray *foods) {
    int count = (int)[foods count];
    return count;
}

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        NSArray *foods = @[@"Chicken Fry", @"Soup", @"Biriani", @"Snacks"];
        NSLog(@"%d", getCount(foods));    }
    return 0;
}

getCount() ফাংশনটি আর্গুমেন্ট হিসেবে NSArray অবজেক্ট (Objective-C তে একটি অ্যারেটাইপ ভ্যারিয়েবল যাতে আমরা রেষ্টুরেন্টের খাবারের লিস্ট রাখছি) নেয় এবং integer টাইপ ডাটা (মোট আইটেমের সংখ্যা) রিটার্ন করে।

int count = (int)[foods count];

এই লাইন টি দেখে ঘাবড়ানোর কিছু নেই। foods অ্যারেতে কতগুলো ইলিমেন্ট আছে সে সংখ্যাটি count রিটার্ন করে। count হচ্ছে food অবজেক্টের একটি বিল্ট-ইন মেথড যেটা আসলে NSArray ক্লাসে ডিফাইন করা আছে। পরবর্তীতে বিস্তারিত দেখানো হবে।

ডিক্লেয়ারেশন বনাম ইম্প্লিমেন্টেশনঃ যেখানে একটি ফাংশনকে কল করে ব্যবহার করা হবে তার পুর্বেই কোথাও ফাংশনটিকে ডিফাইন করতে হয়। অর্থাৎ ব্যবহারের পুর্বেই কম্পাইলার কে জানাতে হবে যে getCount() নামে একটি ফাংশন আছে এবং এই ফাংশনটির কাজের বর্ণনা তথা ডেফিনিশন/ইম্প্লিমেন্টেশনও ঠিক করা আছে। উপরের কোড এ getCount() ফাংশনটি main() এর আগে না লিখে পরে লিখে main() এর ভিতর থেকে কল করলে কম্পাইলার ফাংশনটিকে খুজে পেত না।

ফাংশনের ডিক্লেয়ারেশন কম্পাইলারকে ফাংশনটির ইনপুট, আউটপুট সম্পর্কে জানায়। শুধুমাত্র রিটার্ন ডাটার ডাটাটাইপ ও প্যারামিটারস পাঠিয়ে কম্পাইলার চেক করতে পারে যে ফাংশনটির ব্যবহার ঠিকমত হচ্ছে কিনা। উদাহরনস্বরুপ বলা যায়, ডিক্লেয়ারেশন এ রিটার্ন টাইপ হিসেবে ঠিক করা আছে int কিন্তু আসলে কোডব্লক এ ডাটা পাঠাচ্ছে double, তাহলে কম্পাইলার এই ভুলটা ধরতে পারে। এই ডিক্লেয়ারেশন এর সাথে একটি কোডব্লক (ইমপ্লিমেন্টেশন) এটাচ করলেই তা হয়ে যায় সম্পুর্ন ডেফিনিশন। নিচের কোডটি দেখলেই ব্যপারটি পরিস্কার হয়ে যাবে,

// main.m
#import 

// Declaration
int getCount(NSArray *);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *foods = @[@"Chicken Fry", @"Soup", @"Hydrabadi Biriani", @"Snacks"];
        NSLog(@"%d", getCount(foods));
    }
    return 0;
}

// Implementation
int getCount(NSArray *foods) {
    int count = (int)[foods count];
    return count;
}

এখানে আগে ফাংশনটিকে ডিক্লেয়ার করা হয়েছে এবং এটার ব্যাপারে কম্পাইলারকে জানানো হচ্ছে এবং পরে যেকোনো এক জায়গায় সেটার পূর্ণ ইমপ্লিমেন্টেশন লেখা হয়েছে। উল্লেখ্য, রিটার্ন টাইপে সকল ধরনের বেসিক ডাটাটাইপ, পয়েন্টার ও অবজেক্ট হতে পারে। কিন্তু খেয়াল রাখতে হবে যে, ফাংশন শুধু মাত্র একটি ইনফরমেশনই রিটার্ন করতে পারে। হতে পারে সেটি একটি অবজেক্ট অথবা একটি অ্যারে অথবা একটি পয়েন্টার অথবা অন্য যেকোন ডাটাটাইপ।

ফাংশন ওভারলোডিংঃ C এর মত Objective-C তেও ফাংশন ওভারলোডিং নাই। আসলে অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং এর প্রধান যে ৩ টি ফিচার আছে তার মধ্যে একটি হল পলিমরফিজম। ফাংশন ওভারলোডিং পলিমরফিজম এরই উদাহরন। পরবর্তীতে অধ্যায় গুলো এ সম্পর্কে বিস্তারিত আলোচনা হবে। নিচের উদাহরনটি দেখলেই বুঝা যাবে। Screen Shot 2014-05-09 at 10.40.28 PM

Static কি-ওয়ার্ড ঃ Static কি-ওয়ার্ডটি ফাংশন অথবা ভ্যারিয়েবল এর অ্যাভেইলাবিলিটি (Availibility) অল্টার করার সুবিধা দেয়। মুলত এই কিওয়ার্ডের এফেক্ট নির্ভর করে কোন ক্ষেত্রে এর ব্যবহার হচ্ছে তার ওপর। Static কিওয়ার্ডটি ফাংশন অথবা ভ্যারিয়েবল ডিক্লারেশন এ ব্যবহার করা হয়। এই অংশে স্ট্যাটিক কিওয়ার্ডের ব্যবহার দেখানো হবে।

স্ট্যাটিক ফাংশন (Static Function)ঃ স্বাভাবিকভাবেই সকল ফাংশনই গ্লোবাল স্কোপে থাকে। এ কথার মানে হল কোন ফাইলে ফাংশন ডিফাইন করার সাথে সাথে তা অন্য সকল ফাইল থেকে কল করা যায় যদি ওই ফাংশন ওয়ালা ফাইলটি কলার (caller) ফাইলে ইনক্লুড করে নেয়া হয়। ধরে নেই আমাদের প্রজেক্টে ৩ টা ফাইল আছে। কোন একটি ফাইলে যদি কোন ফাংশনের ডেফিনেশন থাকে তাহলে অন্য সকল ফাইল থেকে এই ফাংশন কে ব্যবহার করা যাবে। যদি আমরা চাই যে এই ফাংশনটি শুধুমাত্র এই ফাইল থেকেই ব্যবহার করা যাবে, অর্থাৎ এই ফাংশনটি এই ফাইলের প্রাইভেট ফাংশন হবে তাহলে ফাংশনটিকে স্ট্যাটিক হিসেবে ডিক্লেয়ার করলেই কাজটি হয়ে যাবে। বিভিন্ন ফাইলে যদি একই নামের ফাংশন থাকে তারপর ও কনফ্লিক্ট করবে না।

স্ট্যটিক ফাংশনের বেসিক সিনট্যাক্স নিচের উদাহরনে দেখানো হল। এই কোডটুকু যদি অন্য কোন ফাইলে লিখা হয় যেমন কোন ফাংশন লাইব্রেরীতে, তাহলে কখনোই main.m এর মধ্যে থেকে getRestaurantMenu() ফাংশনটিকে এক্সেস/কল করা যাবে না।

// Static function declaration
static NSArray getRestaurantMenu();

// Static function implementation
static int getRestaurantMenu() {
    NSArray *menuList = @[@"Chicken Fry", @"Soup", @"Biriani", @"Snacks"];
    return (int)[menuList count];
}

বিশেষভাবে উল্লেখ্য যে, static কিওয়ার্ডটি ডিক্লারেশন এবং ইমপ্লিমেন্টেশন উভয় ক্ষেত্রেই ব্যবহার করতে হবে।

স্ট্যাটিক লোকাল ভ্যারিয়েবলঃ কোন ফাংশনের ভেতরে ডিক্লেয়ার করা ভ্যারিয়েবল কে ওই ফাংশনের লোকাল ভ্যারিয়েবল বলা হয়। অর্থাৎ ভ্যারিয়েবলটির স্কোপ হবে ওই ফাংশনের ভেতরের অংশটুকুই। যতবার এই ফাংশনটি কল করা হবে ততবারই ভ্যারিয়েবলটি নতুন করে রি-সেট হবে। “নতুন করে রি- সেট” হওয়া বলতে বোঝানো হচ্ছে যে, আগের বার কল হওয়ার সময় ভ্যারিয়েবলটির আপডেটেড ভ্যালু, মেমোরি লোকেশন পরেরবারের সাথে মিলবে না। প্রত্যেক বার ফাংশনটি কল হবার সময় ভ্যারিয়েবলটির নতুন করে মেমোরি লোকেশন সেট হবে এবং সেটির প্রাথমিক ইনিসিয়ালাইজড মান সেট হবে, তথা রিসেট হবে।

#import 

int getNumberOfChicken()
{
    int count = 0;
    count = count + 1;
    return count;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog( @"%d", getNumberOfChicken());   // 1
        NSLog( @"%d", getNumberOfChicken());   // 1
    }
    return 0;
}

উপরের কোডে getNumberOfChicken() ফাংশনটিতে count ভ্যারিয়েবলটি স্ট্যাটিক না। তাই যতবারই main() থেকে getNumberOfChicken() কে কল করা হবে, ততবারই নতুন করে count ডিক্লেয়ার হবে (নতুন মেমরী লোকেশন এবং ভ্যালু ০)।

কিন্তু নিচের মত করে, এই ভ্যারিয়েবলটি ডিক্লেয়ার করার সময় যদি স্ট্যাটিক হিসেবে ডিক্লেয়ার করা হয় তাহলে প্রত্যেকবার কল হওয়ার সময় নতুন করে রি-সেট হবে না। আগের বার ফাংশনটি এক্জিকিউট হওয়ার পর ভ্যারিয়েবলটির যা ভ্যালু ছিল তাই থাকবে কারন মেমরী লোকেশন আগেরটাই থাকবে।

#import 

int getNumberOfChicken()
{
    static int count = 0;
    count = count + 1;
    return count;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog( @"%d", getNumberOfChicken());   // 1
        NSLog( @"%d", getNumberOfChicken());   // 2
    }
    return 0;
}

ভ্যারিয়েবলটি static হিসেবে ডিক্লেয়ার করার কারনে শুধুমাত্র প্রথমবার count ভ্যারিয়েবলের জন্য মেমরী লোকেশন সেট হয় এবং ০ দিয়ে ইনিশিয়ালাইজ হয়। কিন্তু পরবর্তীতে যতবারই কল করা হোক না কেন count এর ভ্যালু ওই মেমরী লোকেশন থেকেই ইনভোক করা হয়।

ফাংশনের স্ট্যাটিক লোকাল ভ্যারিয়েবল গুলো যতবারই কল করা হোক না কেন, সাধারন লোকাল ভ্যারিয়েবলের মত বার বার ওই ভ্যারিয়েবল রিসেট হয় না। ফাংশন ওই ভ্যারিয়েবল শেষ অবস্থার ডাটা মনে রাখে। কিন্তু স্ট্যাটিক ফাংশনের মত স্ট্যাটিক লোকাল ভ্যারিয়বলের স্কোপের কোন পরিবর্তন হয় না। স্ট্যাটিক লোকাল ভ্যারিয়েবল শুধু মাত্র ওই ফাংশনের স্কোপেই ব্যবহার করা যাবে।

ফাংশন লাইব্রেরীঃ অবজেক্টিভ-সি নেমস্পেস সাপোর্ট করে না। অন্যান্য গ্লোবাল ফাংশন গুলোর সাথে যাতে নাম নিয়ে কোন কনফ্লিক্ট না হয় সে জন্য ফাংশন এবং ক্লাসগুলোতে ইউনিক আইডেন্টিফায়ার হিসেবে প্রিফিক্স যোগ করে দেওয়া হয়। ফলস্বরুপ শুধু CaptureSession এর পরিবর্তে AVCaptureSession(), শুধু Layer এর পরিবর্তে CALayer() ইত্যাদি ফাংশন দেখা যায়।

তাই আমরা যখন নিজেদের লাইব্রেরী বানাবো তখন ফাংশনের নাম গুলো একটা হেডার (.h) ফাইলে ডিক্লেয়ার করব এবং আলাদা ইমপ্লিমেন্টেশন (.m) ফাইলে ফাংশনগুলোর ইমপ্লিমেন্টেশন করব। Objectiv-C এর ক্লাস গুলোর গঠনও এরকম। এই পদ্ধতিতে ফাইল দুটোকে যথাক্রমে ইন্টারফেস ফাইল এবং ইমপ্লিমেন্টেশন ফাইল বলা হয়। এতে করে ওই লাইব্রেরী ব্যবহার করার সময় ইমপ্লিমেন্টেশন না ভেবে শুধুমাত্র হেডার ফাইলকে ইমপোর্ট করেই কাজ করা যাবে। উল্লেখ্য, হেডার ফাইলের এই ধরনের ব্যবহারকে একটা API লেয়ার হিসেবেও কল্পনা করা যায়। অর্থাৎ আমরা আরেকটা ক্লাসের সাথে যোগাযোগের জন্য সেটার এক্সেস-অ্যাবল লেয়ার তথা ইন্টারফেস ফাইলের সাথে যোগাযোগ করবো, মুল ইমপ্লেমেন্টেশন ফাইলের সাথে নয়।

//  RestaurantUtilities.h
#import 

NSString *RUgetRestaurantTitle();
int RUgetCountOfItems();

এই হেডার ফাইলের ইম্পলিমেন্টেশন ফাইলটি নিচের মত হতে পারে,

//  RestaurantUtilities.m
#import "RestaurantUtilities.h"

NSString *RUgetRestaurantTitle()
{
    return @"ভুতের আড্ডা";
}

int RUgetCountOfItems()
{
    NSArray *items = @[@"সরষে ইলিশ", @"খিচুরী"];
    return (int)[items count];
}

এখন main.m এ RestaurantUtilities.h হেডার ফাইল কে ইমপোর্ট করে সকল ফাংশনগুলোকে ব্যবহার করতে পারবো। এখানে উল্লেখযোগ্য যে RestaurantUtilities.m এ যদি কোন static ফাংশনের ডিক্লারেশন এবং ইমপ্লিমেন্টেশন থেকে থাকত তাহলে কিন্তু main.m থেকে সেই ফাংশনগুলো ব্যবহার করতে চাইলে কম্পাইলার এরর হবে, যার ব্যাখ্যা আগেই দেয়া হয়েছে।

//  main.m
#import 
#import "RestaurantUtilities.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        NSLog( getRestaurantTitle());     // ভুতের আড্ডা
        NSLog( @"%d", getCountOfItems()); // 2

    }
    return 0;
}

পরের চ্যাপ্টারঃ এই চ্যাপ্টারে C এবং Objective-C তে ফাংশনের ব্যবহার, সাথে এদের এবং ভেরিয়েবলের স্কোপ, ডিক্লেয়ারেশন, ইমপ্লেমেন্টেশন, ইন্টারফেস/হেডার ফাইল, ইমপ্লেমেন্টেশন ফাইল ইত্যাদি বিষয় নিয়ে আলোচনা হয়েছে।

SHARE