for লুপ

May 12, 2013

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

প্রোগ্রামিং এর ক্ষেত্রেও দেখা যায় একই ধরনের কাজ, কেবল কিছু মানের পরিবর্তন ঘটিয়ে বারবার করতে হয়। তখন কাজটির জন্য লুপ ব্যবহার করা হয়। এতে যে কাজগুলো বারবার করতে হবে সেগুলো একসাথে লিখে যতবার করতে হয় ততবারের একটি লুপ চালালেই হয়ে গেল! এখন দেখা যাক ব্যাপারটির জন্য আমাদের C ল্যাংগুয়েজে কি আছে।

C তে তিন ধরণের লুপ আছে
১। for লুপ
২। while লুপ
৩। do-while লুপ

৩ নাম্বারটির কাজ প্রথম দুটি দিয়েই করা যায়। আসলে কোন একটি লুপ চালানোর জন্য প্রথম দুটির একটি ব্যবহার করলেই হয়। দুটি দিয়েই লুপের সব কাজ করা যায়। সিন্ট্যাক্স এর দিক থেকে দুটি ভিন্ন। অর্থাৎ দুটি লেখার ধরণ ভিন্ন। তবে কাজ একই। লেখার ধরণ ভিন্ন হওয়ায় যার যখন যেভাবে লিখতে সুবিধা সে তখন সেই অনুযায়ী লুপ ব্যবহার করে। বেশিরভাগ ক্ষেত্রেই সাধারণত for লুপ ব্যবহার করা হয়। এখানে সেটি নিয়েই আলোচনা করা হল।

প্রথমেই দেখা যাক for এর সিন্ট্যাক্স কিরকম।

for( initialization ; condition ; increment/decrement ) {
    /* যে কাজগুলো বারবার করতে হবে */
}

এখানে for এর পরে প্রথম ব্র্যাকেটের মধ্যে তিনটি অংশ লক্ষনীয়। তিনটি অংশ দুইটি সেমিকোলনের মাধ্যমে আলাদা করা আছে। এই ব্যাপারটা গুরুত্বের সাথে মাথায় রাখতে হবে যে এটি C এর for লুপের সিন্ট্যাক্স। অতএব যখনই for লুপ লেখা হবে, তখনই দুইটি সেমিকোলনের মাধ্যমে আলাদা করা তিনটি অংশ থাকতেই হবে, এগুলোতে কিছু লেখা না থাকলেও সেমিকোলন দুইটি থাকতেই হবে। একটি কম হলেও হবে না, বেশি হলেও হবে না।

এখন দেখা যাক ব্র্যাকেটের মধ্যে কি আছে। ব্র্যাকেটের মধ্যের যে তিনটি অংশ তার প্রথমটি দেখা যাচ্ছে initialization । এই অংশটি লুপ শুরু হওয়ার সময় সংঘটিত হয়। এখানে সাধারণত একটি এসাইনমেন্ট স্টেটমেন্ট থাকে। কোন একটি ভেরিয়েবলে, যেই ভেরিয়েবল দিয়ে লুপটি চলবে, তাকে একটি মান দেওয়া হয়। এর পরের অংশ হল condition । এখানে কোন একটি শর্ত থাকবে। এরপরে increment/decrement এ কোন একটি ভেরিয়েবলের মান বাড়ানো হয় বা কমানো হয়। এতে করে প্রতিবার লুপ চলার সাথে সাথে লুপটি শর্তের দিকে আগাতে থাকে এবং এক সময় শর্ত মিথ্যা হয়ে লুপ থেমে যায়।

এবার দেখি কিভাবে এটি কাজ করে। একটি উদাহরণ দেখি। ধরা যাক আমাদেরকে একই লেখা বারবার প্রিন্ট করতে হবে। আমরা প্রিন্ট করতে চাই "This is a C program." । এই লেখাটা দশবার প্রিন্ট করতে চাই। তাহলে আমরা printf() এর মধ্যে দশবার এটি লিখতে পারি। আর যদি কষ্ট কম করতে চাই তাহলে একটা লুপ ব্যবহার করে দশবার প্রিন্ট করে দিতে পারি। লুপের মাধ্যমে লিখলে প্রোগ্রামটি হবে এরকমঃ

int i;
for( i = 1; i <= 10; i++ ) {
    printf( "This is a C program.\n" );
}

এখানে প্রথমে i একটি ভেরিয়েবল ডিক্লেয়ার করা হয়েছে। এটি দিয়ে লুপ চালানো হবে। অর্থাৎ কতবার একটি কাজ হবে সেটি নির্দিষ্ট করে দিব। প্রথমে initialization অংশে প্রোগ্রাম যাবে। সেখানে যে স্টেটমেন্ট থাকবে সেটি সম্পন্ন হবে। এখানে আছে i = 1 । অর্থাৎ i এর মান 1 হয়ে গিয়েছে। এই কাজটি একবারই হবে। একবার হয়ে যাওয়ার পর যতবার লুপ চলবে এটি আর হবে না। অর্থাৎ initialization / প্রথম সেমিকোলনের আগের অংশে যা থাকবে সেটি পুরো লুপ চলার শুরুতে সম্পন্ন হবে এবং আর কখনই হবে না।

এরপরের যে অংশটি আছে সেটি হল শর্ত। Initialization হওয়ার ঠিক পরপরই এখানে এসে প্রোগ্রাম দেখবে যে শর্ত দেওয়া আছে সেটি সত্য কিনা। শর্ত বিভিন্নভাবে দেওয়া যেতে পারে। এটার সাথে initialization এর সম্পর্ক থাকতেও পারে, নাও থাকতে পারে। শর্ত আছে, সেটি সত্য নাকি মিথ্যা এটিই দেখার বিষয়। যদি শর্ত সত্য না হয় তাহলে লুপের ভিতরে প্রবেশ করবে না, যেমনটি if এর ক্ষেত্রে হয়। সরাসরি লুপের পরে যে স্টেটমেন্ট আছে সেখানে চলে যাবে। আর যদি শর্ত সত্য হয় তাহলে তখনই লুপের ভিতরে প্রবেশ করবে। ভিতরে যে স্টেটমেন্টগুলো আছে সেগুলো সম্পাদিত হবে। এখানে আছে i <= 10 । যেহেতু প্রথমেই i এর মান 1 করে নেওয়া হয়েছে অতএব এখন শর্তটি সত্য। তাহলে লুপের ভিতরে প্রবেশ করবে এবং printf() সম্পাদিত হবে।

স্টেটমেন্টগুলো সম্পাদিত হয়ে গেলে প্রোগ্রাম increment/decrement অংশে আসবে। এখানে যে স্টেটমেন্ট লেখা থাকবে সেটি সংঘটিত হবে। অর্থাৎ উপর্যুক্ত উদাহরণ অনুযায়ী i++ হবে। i এর মান এক বাড়বে। এরপর প্রোগ্রাম আবার শর্ত পরীক্ষা করে দেখবে। শর্ত সত্য হলে আগেরমতই লুপের ভিতর প্রবেশ করবে। আরেকবার printf() হবে। আবার increment/decrement অংশে যাবে। i এর মান এক বাড়বে। এরপর শর্ত পরীক্ষা করবে। printf() হবে। এভাবে চলতে থাকবে। এখানে খেয়াল রাখা উচিৎ, initialization অংশটি আর আসবে না। একেবারে শুরুতে একবার হবে, তখনই শেষ।

এভাবে লুপটি চলতে থাকবে এবং i এর মান এক এক করে বাড়বে এবং প্রিন্ট হবে। i এর মান যখন 10 হবে তখন শর্ত i <= 10 সত্য হয়ে প্রিন্ট হয়ে i++ হয়ে i এর মান 11 হবে। এবার যখন শর্ত পরীক্ষা করতে যাবে তখন শর্ত মিথ্যা হবে। ফলে লুপ থেকে প্রোগ্রাম বের হয়ে আসবে। এভাবে দশবার একই কাজ সম্পাদিত হল। এখানে লক্ষনীয়, যখন লুপের শর্ত মিথ্যা হল তখন i এর মান 11। এই ধারণাটি বিভিন্ন ক্ষেত্রে প্রয়োজন হয়। এটি বুঝতে অনেকেরই সমস্যা হয়। একটু ভালো মত খেয়াল করলেই বুঝা যাবে। কোন কাজের পর কোনটি হচ্ছে সেটি খেয়াল করতে হবে। i++ হয়ে, অর্থাৎ i এর মান এক বেড়ে যাওয়ার পর শর্ত মিথ্যা হয়েছে, অতএব ইতোমধ্যে i এর মান 11 হয়েছে।

এগুলো একেবারেই প্রাথমিক পর্যায়ের কথাবার্তা। আরেকটু আগানো যাক। for লুপের ব্র্যাকেটের ভিতরের যে তিনটি অংশ আছে সেগুলোতে যে কিছু লেখা থাকতে হবে এমন কোন কথা নেই। তবে লেখা না থাকলেও সেমিকোলন দুইটা থাকতেই হবে। ধরা যাক, আমার initialization করার দরকার নেই। অথবা লুপের আগেই আমি সেটা করে ফেলেছি। তাহলে প্রথম অংশ খালি রাখলেই চলবে। যেমন,

i = 1;
for( ; i <= 10; i++ ) {
    printf( "This is a C program.\n" );
}

এক্ষেত্রে প্রথম অংশটি খালি রাখা হয়েছে। কারণ আগেই i এর মান 1 করে নেওয়া হয়েছে। আবার একইভাবে শর্তের অংশটিও খালি রাখা যায় যদি লুপটি অন্য কোনভাবে (যেমন break ব্যবহার করে) থামানোর ব্যবস্থা করা থাকে। একইভাবে increment/decrement অংশটিও খালি রেখে দেওয়া যায়। যেমন, যদি for এর ভিতরেই printf() এর পরে, একেবারে শেষে i++ করা হয়, তাহলে increment/decrement অংশে কিছু না লেখলেও হচ্ছে। এমনকি চাইলে প্রতিটি অংশই ফাঁকা রেখে দেওয়া সম্ভব! তবে এক্ষেত্রে লুপ থামানোর কোন ব্যবস্থা না করলে তা ইনফিনিট লুপে পরিণত হবে, অর্থাৎ লুপ চলতেই থাকবে। কখনই থামবে না।

একটু লক্ষ্য করলে দেখা যাবে প্রথম এবং শেষ অংশে যা লেখা হয়েছে সেগুলো মূলত একেকটি স্টেটমেন্ট। অর্থাৎ এখানে যেকোন ধরণের স্টেটমেন্ট লেখা যাবে এবং যেটি লেখা হবে প্রোগ্রাম ঐখানে গেলে সেটি সম্পাদিত হবে। অতএব উপরের উদাহরণটিকে চাইলে এভাবেও লেখা যায়ঃ

int i;
for( i = 1; i <= 10; printf( "This is a C program.\n" ) ) {
    i++;
}

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

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

এবার লুপের প্রয়োগের সাধারণ একটা উদাহরণ দেখা যাক। লুপের মাধ্যমে নিচের প্রবলেমটি সমাধান করতে হবে। একটা নাম্বার ইনপুট দেওয়া হবে। যত নাম্বার ইনপুট দেওয়া হবে আউটপুটে ততগুলা লাইন হবে এবং প্রতি লাইনে ঐ লাইন নাম্বারের সমান সংখ্যক * থাকবে।

input:              input:
7                   5
output:             output:
*                   *
**                  **
***                 ***
****                ****
*****               *****
******
*******

এটার কোড কিরকম হবে? আগে নিজে চিন্তা করে দেখ সমাধান করতে পার নাকি। সময় নাও। নিচে আমার সমাধানের কোড দিলাম।

int n, i, j;
scanf( "%d", &n );
for( i = 1; i <= n; i++ ) {
   for( j = 1; j <= i; j++ ) {
      printf( "*" );
   }
   printf( "\n" );
}

কিভাবে কি হচ্ছে দেখ। প্রথমে সমস্যাটার সমাধানের জন্য কি করতে হবে তা চিন্তা করতে হবে। এখানে বলা হয়েছে কতগুলো লাইন হবে তা ইনপুট দেওয়া হবে এবং ততগুলো লাইন প্রিন্ট করতে হবে। তার মানে প্রতিটা লাইন প্রিন্ট করার জন্য একটা লুপ চালাতে হবে। সেই জন্য আমরা প্রথম লুপটা লিখলাম। সেখানে i মানে লাইন নাম্বার, 1 থেকে শুরু হয়ে n পর্যন্ত লাইন হবে। প্রতি লাইনে আবার লাইনের নাম্বারের সমান সংখ্যক * প্রিন্ট করতে হবে। অর্থাৎ প্রতি লাইনের জন্য আবার একটা করে লুপ চলবে। এটা হল Nested Loop। মানে লুপের ভিতরে লুপ। এখানে আমরা আরেকটি ভেরিয়েবল j নিলাম যার মধ্যে * এর সংখ্যা থাকবে। এটা 1 থেকে শুরু করে লাইন যত তত নাম্বার পর্যন্ত অর্থাৎ i পর্যন্ত যাবে। এই লুপ দিয়ে একটা লাইনে যতগুলা * প্রিন্ট করতে হবে সেটি হবে। এরপর এক লাইনে সবগুলা * প্রিন্ট করা হয়ে গেলে আমরা একটা ‘\n’ এর মাধ্যমে এর পরের লাইনে চলে যাব

আবার যদি এর উল্টোটা করতে বলা হত?

input:              input:
7                   5
output:             output:
*******             *****
******              ****
*****               ***
****                **
***                 *
**
*

আবারও একই কথা। আগে নিজে চিন্তা কর। সময় নাও। তারপর না পারলে নিচের কোড দেখ।

int n, i, j;
scanf( "%d", &n );
for( i = 1; i <= n; i++ ) {
   for( j = n; j >= i; j-- ) {
      printf( "*" );
   }
   printf( "\n" );
}

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

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

১) 1 <= x <= 50 সীমার মধ্যে সকল জোড় সংখ্যা প্রিন্ট কর।
২) 1 <= x <= 50 সীমার মধ্যে সকল বিজোড় সংখ্যা প্রিন্ট কর।
৩) 10 <= x <= 70 সীমার মধ্যে 3 দ্বারা নিঃশেষে বিভাজ্য সকল সংখ্যা প্রিন্ট কর।
৪) 44 <= x <= 89 সীমার মধ্যে 3 দ্বারা নিঃশেষে বিভাজ্য সংখ্যা বাদে বাকি সকল সংখ্যা প্রিন্ট কর।
৫) 10 <= x <= 101 সীমার মধ্যে সকল মৌলিক সংখ্যা প্রিন্ট কর।
৬) উপরের সবগুলো প্রবলেম উলটো দিকে (সীমার শেষ থেক শুরুর দিকে) প্রিন্ট কর।
৭) N ইনপুট দেওয়া হবে। এরপর N সংখ্যক নাম্বার ইনপুট দেওয়া হবে। এদের যোগফল প্রিন্ট কর।
৮) N ইনপুট দেওয়া হবে। এরপর N সংখ্যক নাম্বার ইনপুট দেওয়া হবে। এদের মধ্যে বিজোড় সংখ্যাগুলোর যোগফল প্রিন্ট কর।
৯) N ইনপুট দেওয়া হবে। এরপর N সংখ্যক নাম্বার ইনপুট দেওয়া হবে। এদের average প্রিন্ট কর।
10) 1 <= x <= 10 সীমার মধ্যে প্রতিটা সংখ্যার ফ্যাক্টরিয়াল (x!) প্রিন্ট কর।