No history yet

Advanced Logic Building

پیچیدہ منطقی ڈھانچے

جب آپ C++ کے بنیادی اصول سیکھ لیتے ہیں، جیسے ویری ایبلز، ڈیٹا ٹائپس، اور سادہ لوپس، تو اگلا قدم زیادہ پیچیدہ اور موثر منطقی ڈھانچے بنانا ہوتا ہے۔ یہ صرف کوڈ لکھنے کے بارے میں نہیں ہے، بلکہ ایسا کوڈ لکھنے کے بارے میں ہے جو تیز، پڑھنے میں آسان، اور میموری کے لحاظ سے موثر ہو۔ اس سیکشن میں، ہم بنیادی سینٹیکس سے آگے بڑھ کر ان تکنیکوں پر توجہ مرکوز کریں گے جو آپ کی پروگرامنگ کی مہارت کو بہتر بنائیں گی۔

گہرائی میں نیسٹڈ لوپس

آپ شاید نیسٹڈ لوپس (nested loops) سے واقف ہوں گے، جہاں ایک لوپ دوسرے لوپ کے اندر چلتا ہے۔ یہ عام طور پر میٹرکس یا 2D اریے پر کام کرنے کے لیے استعمال ہوتے ہیں۔ لیکن ان کا استعمال اس سے کہیں زیادہ ہے۔ مثال کے طور پر، دو اریز (arrays) میں مشترکہ عناصر تلاش کرنے کے لیے ایک پیچیدہ نیسٹڈ لوپ استعمال کیا جا سکتا ہے۔

تاہم، ہر نیسٹڈ لوپ آپ کے پروگرام کی کو بڑھاتا ہے۔ اگر بیرونی لوپ n بار چلتا ہے اور اندرونی لوپ m بار چلتا ہے، تو کل آپریشنز کی تعداد n * m ہو گی۔ اگر دونوں لوپس ایک ہی سائز کے ان پٹ پر منحصر ہوں، تو کمپلیکسٹی O(n²) ہو جاتی ہے، جس کا مطلب ہے کہ ان پٹ کا سائز بڑھنے پر ایگزیکیوشن کا وقت تیزی سے بڑھ جائے گا۔

#include <iostream>
#include <vector>

int main() {
    std::vector<int> arr1 = {1, 5, 9, 12};
    std::vector<int> arr2 = {9, 10, 5, 14};
    std::cout << "Common elements are: ";

    // O(n*m) کمپلیکسٹی والا نیسٹڈ لوپ
    for (int i = 0; i < arr1.size(); ++i) {
        for (int j = 0; j < arr2.size(); ++j) {
            if (arr1[i] == arr2[j]) {
                std::cout << arr1[i] << " ";
            }
        }
    }
    std::cout << std::endl;
    return 0;
}

اس کوڈ میں، ہر ایلیمنٹ کا دوسرے ایلیمنٹ سے موازنہ کیا جاتا ہے، جو چھوٹے ڈیٹا سیٹس کے لیے تو ٹھیک ہے، لیکن بڑے ڈیٹا سیٹس کے لیے بہت سست ہو سکتا ہے۔ بعد میں آپ بہتر الگورتھم سیکھیں گے (جیسے ہیشنگ) جو اس کام کو بہت تیزی سے کر سکتے ہیں۔

سوئچ-کیس کی اصلاح

switch اسٹیٹمنٹ صرف if-else if-else کا متبادل نہیں ہے۔ کچھ خاص حالات میں، یہ بہت زیادہ موثر ہو سکتا ہے۔ جب آپ switch کو انٹیجر (integer) یا کریکٹر (char) جیسی مستقل ویلیوز کے ایک سیٹ کے ساتھ استعمال کرتے ہیں، تو کمپائلر اسے ایک میں تبدیل کر سکتا ہے۔

جمپ ٹیبل ایک look-up ٹیبل کی طرح کام کرتا ہے، جو پروگرام کو سیدھا صحیح کوڈ بلاک پر بھیج دیتا ہے، بجائے اس کے کہ وہ ہر شرط کو ایک ایک کرکے چیک کرے۔

#include <iostream>

void processOption(int option) {
    switch (option) {
        case 1:
            std::cout << "Option 1 selected: Profile" << std::endl;
            break;
        case 2:
            std::cout << "Option 2 selected: Settings" << std::endl;
            break;
        case 3:
            std::cout << "Option 3 selected: Logout" << std::endl;
            break;
        default:
            std::cout << "Invalid option!" << std::endl;
            break;
    }
}

int main() {
    processOption(2);
    return 0;
}

اس کے برعکس، if-else کی زنجیر ہر شرط کو ترتیب وار چیک کرے گی۔ اگر آپ کے پاس بہت سے case ہوں اور آپ کا مطلوبہ کیس آخر میں ہو، تو switch نمایاں طور پر تیز ہو سکتا ہے۔ تاہم، switch کم لچکدار ہوتا ہے؛ یہ رینج (مثلاً x > 10) یا پیچیدہ منطقی اظہار کو ہینڈل نہیں کر سکتا۔

ویری ایبلز کا دائرہ کار (Scope)

ویری ایبل کا اسکوپ (scope) اس بات کا تعین کرتا ہے کہ کوڈ میں اسے کہاں تک رسائی حاصل کی جا سکتی ہے۔ C++ میں، اسکوپ عام طور پر کرلی بریکٹ {} کے جوڑے سے بیان ہوتا ہے۔ ایک ویری ایبل جو کسی فنکشن کے اندر بنایا گیا ہے وہ اس کا ہوتا ہے اور وہ صرف اسی فنکشن کے اندر استعمال ہو سکتا ہے۔

ایک اچھی پریکٹس یہ ہے کہ ویری ایبلز کو ہر ممکن حد تک محدود اسکوپ میں رکھا جائے۔ اس سے نہ صرف ناموں کے تصادم (naming conflicts) سے بچا جا سکتا ہے، بلکہ یہ میموری کے انتظام میں بھی مدد کرتا ہے۔

#include <iostream>

void myFunction() {
    int functionVar = 100; // صرف myFunction میں قابل رسائی
    std::cout << "Inside function: " << functionVar << std::endl;
}

int main() {
    int mainVar = 50; // صرف main میں قابل رسائی

    for (int i = 0; i < 3; ++i) {
        // 'i' صرف اس for لوپ کے اندر موجود ہے
        std::cout << "Loop variable i: " << i << std::endl;
    }

    // یہاں 'i' کو استعمال کرنے کی کوشش ایک کمپائلر ایرر دے گی
    // std::cout << i << std::endl; // Error!

    myFunction();

    // یہاں 'functionVar' کو استعمال نہیں کیا جا سکتا
    // std::cout << functionVar << std::endl; // Error!

    return 0;
}

اس مثال میں، i ویری ایبل صرف for لوپ کے اندر موجود ہے۔ لوپ ختم ہوتے ہی وہ میموری سے ختم ہو جاتا ہے۔ یہ میموری کو غیر ضروری طور پر استعمال ہونے سے بچاتا ہے اور آپ کے کوڈ کو صاف ستھرا رکھتا ہے۔

اب جب آپ منطق کی ان باریکیوں کو سمجھ گئے ہیں، تو آئیے دیکھتے ہیں کہ آپ اپنی صلاحیتوں کو کیسے جانچ سکتے ہیں۔

Quiz Questions 1/4

اگر ایک نیسٹڈ لوپ میں بیرونی لوپ 'n' بار اور اندرونی لوپ بھی 'n' بار چلتا ہے، تو اس کی ٹائم کمپلیکسٹی کیا ہوگی؟

Quiz Questions 2/4

switch اسٹیٹمنٹ if-else سے زیادہ موثر کیوں ہو سکتا ہے؟

ان تصورات پر عمل کرنا آپ کو ایک بہتر پروگرامر بننے میں مدد دے گا۔ موثر اور پڑھنے کے قابل کوڈ لکھنا اتنا ہی ضروری ہے جتنا کہ کام کرنے والا کوڈ لکھنا۔