[প্রোগ্রামিং বইঃ অধ্যায় শূন্য] শুরুর আগে-
কম্পিউটার তো আসলে গণনা করার যন্ত্র, তাই না? যদিও আমরা এটি দিয়ে গান
শুনি, ভিডিও দেখি, গেমস খেলি, আরও নানা কাজ করি। আসলে শেষ পর্যন্ত
কম্পিউটার বোঝে শূন্য (0) আর একের (1) হিসাব। তাই ব্যবহারকারী (user) যা-ই
করুক না কেন, কম্পিউটার কিন্তু সব কাজ গণনার মাধ্যমেই করে। কম্পিউটারের
ব্যবহার এত ব্যাপক হওয়ার পেছনে অন্যতম কারণ হচ্ছে নানা রকম সফটওয়্যার
দিয়ে নানা ধরনের কাজ করা যায় কম্পিউটারে। এসব সফটওয়্যার তৈরি করতে হয়
প্রোগ্রাম লিখে অর্থাৎ কী হলে কী করবে এটি প্রোগ্রামের সাহায্যে
কম্পিউটারকে বোঝাতে হয়।
একসময় কিন্তু কেবল 0 আর 1 ব্যবহার করেই কম্পিউটারের প্রোগ্রাম লিখতে হতো।
কারণ কম্পিউটার তো 0, 1 ছাড়া আর কিছু বোঝে না, আর কম্পিউটারকে দিয়ে কোনো
কাজ করাতে চাইলে তো তার ভাষাতেই কাজের নির্দেশ দিতে হবে। 0, 1 ব্যবহার করে
যে প্রোগ্রামিং করা হতো, তার জন্য যে ভাষা ব্যবহৃত হতো, তাকে বলা হয় মেশিন
ল্যাঙ্গুয়েজ। তারপর এল অ্যাসেম্বলি ল্যাঙ্গুয়েজ। এতে প্রোগ্রামাররা কিছু
ইনস্ট্রাকশন যেমন ADD (যোগ), MUL (গুণ) ইত্যাদি ব্যবহারের সুযোগ পেল। আর
এই ভাষাকে 0, 1-এর ভাষায় নিয়ে কাজ করাবার দায়িত্ব পড়ল অ্যাসেম্বলারের
ওপর, প্রোগ্রামারদের সে বিষয়ে ভাবতে হতো না। কিন্তু মানুষের চাহিদার তো
শেষ নেই। নতুন নতুন চাহিদার ফলে নতুন নতুন জিনিসের উদ্ভব হয়। একসময় দেখা
গেল যে অ্যাসেম্বলি ল্যাঙ্গুয়েজ দিয়েও কাজ করা ঝামেলা হয়ে যাচ্ছে। তাই
বড় বড় প্রোগ্রাম লিখার জন্য আরও সহজ ও উন্নত নানা রকম প্রোগ্রামিং ভাষা
তৈরি হলো। যেমন - ফরট্রান (Fortran), বেসিক (Basic), প্যাসকেল (Pascal), সি
(C)। তবে এখানেই শেষ নয়, এরপর এল আরও অনেক ল্যাঙ্গুয়েজ, যার মধ্যে
অন্যতম হচ্ছে, সি প্লাস প্লাস (C++), ভিজ্যুয়াল বেসিক (Visual Basic),
জাভা (Java), সি শার্প (C#), পার্ল (Perl), পিএইচপি (PHP), পাইথন (Python),
রুবি (Ruby)। এখনো কম্পিউটার বিজ্ঞানীরা নিত্যনতুন প্রোগ্রামিং ভাষা তৈরি
করে যাচ্ছেন। প্রোগ্রামাররা এসব ভাষা ব্যবহার করে প্রোগ্রাম লেখেন আর
প্রতিটি ভাষার রয়েছে আলাদা কম্পাইলার, যার কাজ হচ্ছে ওই প্রোগ্রামকে
কম্পিউটারের বোধগম্য ভাষায় রূপান্তর করা, তাই এটি নিয়ে প্রোগ্রামারদের
ভাবতে হয় না।
প্রোগ্রাম লিখার সময় প্রোগ্রামারকে তিনটি প্রধান কাজ করতে হয়। প্রথমে তার
বুঝতে হয় যে সে আসলে কী করতে যাচ্ছে, মানে তার প্রোগ্রামটি আসলে কী কাজ
করবে। তারপর চিন্তাভাবনা করে এবং যুক্তি (logic) ব্যবহার করে অ্যালগরিদম
দাঁড় করাতে হয়। মানে, লজিকগুলো ধাপে ধাপে সাজাতে হয়। এর পরের কাজটি
হচ্ছে অ্যালগরিদমটাকে কোনো একটি প্রোগ্রামিং ভাষায় রূপান্তর করা, যাকে
আমরা বলি কোডিং করা। একেক ধরনের কাজের জন্য একেক ল্যাঙ্গুয়েজ বেশি উপযোগী।
এই বইতে আমরা প্রোগ্রামিংয়ের মৌলিক কিছু জিনিস শেখার চেষ্টা করব এবং
প্রোগ্রামগুলো আমরা লিখব সি ল্যাঙ্গুয়েজ ব্যবহার করে। আমি ধরে নিচ্ছি,
তোমরা কম্পিউটার ব্যবহার করে অভ্যস্ত এবং প্রোগ্রামিং জিনিসটার সঙ্গে
সম্পূর্ণ অপরিচিত। আর সি ল্যাঙ্গুয়েজ ব্যবহার করার পেছনে কারণ হচ্ছে, এটি
বেশ পুরোনো হলেও অত্যন্ত শক্তিশালী ও জনপ্রিয় ল্যাঙ্গুয়েজ।
প্রোগ্রামিংয়ের মৌলিক জিনিসগুলো বোঝার জন্য সি ভাষা অত্যন্ত সহায়ক। আর
জনপ্রিয় সব প্রোগ্রামিং প্রতিযোগিতায় যে অল্প কয়েকটি ল্যাঙ্গুয়েজ
ব্যবহার করা যায়, তার মধ্যে সি অন্যতম। আমরা অবশ্য সি ল্যাঙ্গুয়েজের
পুরোটা এখানে শিখব না, কেবল মৌলিক বিষয়গুলো নিয়ে কাজ করতে যা দরকার সেটি
দেখব। এই বইটি পড়ার পরে তোমরা কেবল সি-এর জন্য কোন বই পড়তে পারো অথবা
অন্য কোনো ভাষা (যেমন– সি প্লাস প্লাস, জাভা কিংবা পাইথন) শেখা শুরু করে
দিতে পারো। বইয়ের পরিশিষ্ট অংশে আমি কিছু বইয়ের নাম দিয়েছি, যা তোমাদের
কাজে লাগবে।
বইটি পড়তে তোমাদের তিনটি জিনিস লাগবে, কম্পিউটার (ইন্টারনেট সংযোগ থাকলে
ভালো হয়), সি ল্যাঙ্গুয়েজের কম্পাইলার এবং যথেষ্ট সময়। তাড়াহুড়ো না
করে দুই থেকে তিন মাস সময় নিয়ে বইটি পড়লে ভালো হয়। প্রোগ্রামিং শেখার
জন্য কেবল পড়াই যথেষ্ট নয়, পাশাপাশি কোডিং করতে হবে। বইয়ের প্রতিটি
উদাহরণ নিজে নিজে কোড করে কম্পিউটারে চালিয়ে দেখতে হবে। যখনই আমি কোনো
প্রশ্ন করব, সেটি নিয়ে চিন্তা করতে হবে। তার জন্য যদি দু-তিন ঘণ্টা বা
দু-তিন দিন সময় লাগে লাগুক, কোনো ক্ষতি নেই, বরং দীর্ঘ সময় কোনো সমস্যার
সমাধান নিয়ে চিন্তা করার অভ্যাসটি খুব জরুরি। কোনো অধ্যায় পুরোপুরি বোঝার
আগে পরের অধ্যায় পড়া শুরু করা যাবে না। আবার কোনো অংশ যদি তোমার কাছে
খুব সহজ মনে হয়, সেই অংশ ঠিকভাবে না পড়ে এবং প্রোগ্রামগুলো না করে পরের
অংশে চলে যেয়ো না কিন্তু। সাধারণ পড়ালিখার সঙ্গে প্রোগ্রামিং শেখার অনেক
পার্থক্য। এখানে পড়ার সঙ্গে সঙ্গে কাজ করাও জরুরি। আর এই বই পড়েই কিন্তু
তুমি প্রোগ্রামার হয়ে যাবে না, বইটি পড়ে তুমি প্রোগ্রামার হওয়া শুরু
করবে।
এবার আসা যাক, কম্পাইলার পাবে কোথায়? সি-এর জন্য বেশ কিছু কম্পাইলার আছে।
তুমি যদি লিনাক্স কিংবা ম্যাক ব্যবহারকারী হও, তবে সবচেয়ে ভালো হচ্ছে gcc।
অধিকাংশ লিনাক্সেই এটি আগে থেকে ইনস্টল করা থাকে। তোমার কম্পিউটারে না
থাকলে এটি ইনস্টল করে নিতে হবে। আর উইন্ডোজ ব্যবহার করলে তুমি Codeblocks
(http://www.codeblocks.org/) ব্যবহার করতে পারো। এটি একটি ফ্রি ও ওপেন
সোর্স IDE (Integrated Development Environment) এবং ম্যাক আর লিনাক্সেও
চলে। এমনিতে সাধারণ কোনো টেক্সট এডিটর (যেমন: নোটপ্যাড, জিএডিট, কেরাইট)
ব্যবহার করে কোড লিখে সেটি কম্পাইলার দিয়ে কম্পাইল করে রান করা যায়। তবে
অধিকাংশ আইডিই (IDE) গুলোতেই নিজস্ব টেক্সট এডিটর ও কম্পাইলার থাকে।
প্রোগ্রাম রান করার ব্যবস্থাও থাকে। এ ছাড়াও নানা ধরনের টুলস্ থাকে।
Codeblocksটা সরাসরি তুমি http://www.codeblocks.org সাইট থেকে ডাউনলোড ও
ইনস্টল করতে পারো। Downloads পেইজে Binaries-এ গেলে উইন্ডোজের জন্য তুমি
দুটি অপশন দেখবে: codeblocks-10.05-setup.exe ও
codeblocks-10.05mingw-setup.exe। তুমি দ্বিতীয়টি ডাউনলোড করবে (74.0 MB)।
আর ইনস্টল করার কাজটি অন্য যেকোনো সফটওয়্যার বা গেমসের মতোই। যারা
উবুন্টু ব্যবহার করো, তারা Ubuntu Software Center (Applications >
Ubuntu Software Center) থেকে এটি ডাউনলোড করতে পারো।
প্রোগ্রামিং চর্চার বিষয়। ইন্টারনেটে বেশ কিছু ওয়েবসাইট আছে, যেখানে
প্রচুর সমস্যা দেওয়া আছে যেগুলো প্রোগ্রামের সাহায্যে সমাধান করতে হয়। সব
জায়গাতেই তুমি সি ল্যাঙ্গুয়েজে প্রোগ্রামিং করতে পারবে। এর মধ্যে কিছু
কিছু ওয়েবসাইট আবার নিয়মিত প্রোগ্রামিং প্রতিযোগিতারও আয়োজন করে। এসব
প্রতিযোগিতায় অংশগ্রহণ নিঃসন্দেহে তোমার প্রোগ্রামিং-দক্ষতা বৃদ্ধি করবে
আর সেই সঙ্গে বিশ্বের নানা দেশের প্রোগ্রামারদের সঙ্গে মেশারও সুযোগ করে
দেবে। অবশ্য প্রোগ্রামিং প্রতিযোগিতায় ভালো করতে হলে কেবল প্রোগ্রামিং
জানলেই চলবে না, গাণিতিক দক্ষতাও যথেষ্ট গুরুত্বপূর্ণ। পরিশিষ্ট অংশে
প্রোগ্রামিং প্রতিযোগিতা নিয়ে আলাপ করব।
বইয়ের প্রতিটি প্রোগ্রামের নিচে আমি একটি নম্বর দিয়েছি। প্রোগ্রামের
নম্বর যদি ২.৫ হয়, তার মানে হচ্ছে এটি দ্বিতীয় অধ্যায়ের পাঁচ নম্বর
প্রোগ্রাম।
এটি কিন্তু কোনো গল্পের বই নয়। তাই বিছানায় শুয়ে-বসে পড়া যাবে না। বইটি
পড়ার সময় কম্পিউটার চালু রাখতে হবে এবং প্রতিটি উদাহরণ সঙ্গে সঙ্গে
প্রোগ্রাম লিখে দেখতে হবে, কোনো সমস্যা সমাধান করতে দিলে তখনই সেটি
সমাধানের চেষ্টা করতে হবে। মনে রাখবে, যত বেশি প্রোগ্রামিং তত বেশি আনন্দ।
প্রথম প্রোগ্রাম-
আমরা এখন একটি প্রোগ্রাম লিখে ফেলব,
যেটি তোমার কম্পিউটারের স্ক্রিনে Hello World দেখাবে বা প্রিন্ট করবে। এটি
হচ্ছে প্রোগ্রামিংয়ের একটি ঐতিহ্য। পৃথিবীর অধিকাংশ প্রোগ্রামারই জীবনের
প্রথম প্রোগ্রাম হিসেবে এটি লেখে। আমি এই বইয়ের প্রোগ্রামগুলো চালানোর
জন্য Codeblocks ব্যবহার করব। তবে তোমরা অন্য কিছু ব্যবহার করলেও কোনো
সমস্যা নেই, সবগুলোতে কাজের ধারা মোটামুটি একই রকম। কম্পিউটারে কোডব্লকস
ইনস্টল করে ফেলো। নিজে নিজে ইনস্টল করতে না পারলে নিচের ভিডিও দেখো।
ইনস্টল হয়ে গেল। এখন উইন্ডোজের Start মেনুতে Programs-এ গিয়ে Codeblocks
চালু করো। উবুন্টুতে এটি থাকবে Applications > Programming-এর ভেতর।
-->
এখানে তোমরা Show tips at startup চেকবক্সের টিক (tick) চিহ্নটি উঠিয়ে দিতে পারো।
-->
এখন তোমরা প্রোগ্রামগুলো রাখার জন্য হার্ডডিস্কের ভেতর একটি ফোল্ডার তৈরি
করে নাও। ওই ফোল্ডারে ফাইলগুলো সেভ (Save)করবে। ফাইলের যেকোনো একটি নাম
দাও। আর Save as type হবে C/C++ files।
নিচের ছবিতে দেখো ফাইলের নাম হচ্ছে hello.c। সি প্রোগ্রামের সব ফাইলের এক্সটেনশন হবে .c।
-->
এখানে আমরা আমাদের কোড বা প্রোগ্রাম লিখব। নিচের কোডটি টাইপ করে ফেলো এবং ফাইলটি সেভ করো।
#include <stdio.h>
int main ()
{
printf("Hello World");
return 0;
}
প্রোগ্রাম: ১.১
-->
তোমরা হয়তো চিন্তা করছ, আমি এই হিজিবিজি কী লিখলাম? আস্তে ধীরে সব ব্যাখ্যা করব, চিন্তা নেই! আপাতত আমার কথামতো কাজ করে যাও। এবার Build মেনুতে গিয়ে Compile Current File-এ ক্লিক করো।
-->
তুমি যদি প্রোগ্রামটি ঠিকভাবে টাইপ করে থাকো তবে কম্পাইলার তোমাকে বলবে যে 0 errors, 0 warnings, মানে - প্রোগ্রামে syntax ঠিক আছে।
এখন আবার Build মেনুতে গিয়ে Run-এ ক্লিক করো। তাহলে তোমার প্রোগ্রাম চালু হয়ে যাবে।
এবং তুমি নিচের ছবির মতো স্ক্রিন দেখতে পাবে।
এখানে দেখো, তোমার প্রোগ্রামটি স্ক্রিনে Hello World প্রিন্ট করেছে। পরের লাইনে বলা আছে Process returned 0 (0x0) (এটির অর্থ নিয়ে আমাদের এখন মাথা না ঘামালেও চলবে) আর execution time : 0.031 s মানে প্রোগ্রামটি চলতে 0.031 সেকেন্ড সময় লেগেছে। তারপরের লাইন হচ্ছে, Press any key to continue. কি-বোর্ডে Any key খুঁজে না পেলে অন্য যেকোনো কি চাপলেই চলবে।
তুমি যদি প্রোগ্রামটি ঠিকঠাকভাবে রান করাতে পারো এবং Hello World লেখাটা দেখে থাকো তাহলে তোমাকে অভিনন্দন। তুমি বেশ গুরুত্বপূর্ণ একটি কাজ করে ফেলেছ।
এবারে দেখা যাক আমি কী লিখেছি কোডে।
প্রথম লাইন ছিল: #include <stdio.h>, এটি কেন লিখেছি একটু পরে বলছি।
দ্বিতীয় লাইন ফাঁকা। দেখতে সুন্দর লাগে তাই।
তৃতীয় লাইন: int main()। এটিকে বলে মেইন ফাংশন। সি প্রোগ্রামগুলো মেইন ফাংশন থেকে কাজ করা শুরু করে, তাই সব প্রোগ্রামে একটি (এবং কেবল একটি) মেইন
ফাংশন থাকতে হয়। মেইন ফাংশনের শুরুতে দ্বিতীয় বন্ধনী দিয়ে শুরু করতে
হয় আর শেষও করতে হয় একটি দ্বিতীয় বন্ধনী দিয়ে। শেষ করার আগে আমি return 0; লিখেছি, সেটি কেন এখন ব্যাখ্যা না করলেই ভালো হয়, ফাংশন নিয়ে যখন আলাপ করব তখন বলব। তাই আপাতত তোমরা যেকোনো প্রোগ্রামে নিচের অংশটুকু লিখে ফেলবে:
int main()
{
এখানে কোড থাকবে।
return 0;
}
প্রোগ্রামের পরের লাইন খেয়াল করো: printf("Hello World"); এটি একটি স্টেটমেন্ট। এখানে printf() হচ্ছে একটি ফাংশন যার কাজ হচ্ছে স্ক্রিনে কিছু প্রিন্ট করা। ডবল কোটেশন চিহ্নের ভেতরে যা লিখবে তা-ই স্ক্রিনে সে প্রিন্ট করবে। এই ফাংশনটি স্ক্রিনে প্রিন্ট করে কীভাবে সেটি আসলে বলা আছে stdio.h নামে একটি ফাইলে। এই ফাইলগুলোকে বলে হেডার (header) ফাইল (.h হচ্ছে হেডার ফাইলের এক্সটেনশন)। stdio.h ফাইলে স্ট্যান্ডার্ড ইনপুট আর আউটপুট-সংক্রান্ত যাবতীয় ফাংশন লেখা আছে, আমরা কেবল সেগুলো ব্যবহার করব, ফাংশনগুলো কীভাবে কাজ করে সেটি এখন আমাদের জানার দরকার নেই। আর যেহেতু printf() ফাংশন ব্যবহার করেছি, তাই প্রোগ্রামের শুরুতে #include <stdio.h> লিখতে হয়েছে। এই রকম আরও অনেক প্রয়োজনীয় হেডার ফাইল আছে, যার কিছু আমরা পরবর্তী সময়ে কাজের প্রয়োজনে দেখব।
এখন একটি ব্যাপার খেয়াল করো। printf("Hello World");-এর
শেষে একটি সেমিকোলন রয়েছে। সি ল্যাঙ্গুয়েজে প্রতিটি স্টেটমেন্টের পরেই
একটি সেমিকোলন থাকে। একটি স্টেটমেন্টের কাজ শেষ হলে পরের স্টেটমেন্টের কাজ
শুরু হয়। return 0;ও একটি স্টেটমেন্ট, তাই এটিও সেমিকোলন দিয়ে শেষ করতে হয়েছে। শুরুর দিকে অনেকেই সেমিকোলন দিতে ভুলে যায়, তখন কম্পাইল এরর (compile error) হয়। তোমরা একটি সেমিকোলন মুছে দিয়ে কম্পাইল করার চেষ্টা করে দেখতে পারো।
এবারে একটি খুব গুরুত্বপূর্ণ কথা বলে রাখি। তোমরা কোডটি খেয়াল করলে দেখবে যে আমি #include <stdio.h>, int main(), { ও } যেই লাইনে আছে সেটি এডিটরের একেবারে বাঁ দিক থেকে শুরু করেছি। আর printf এবং return 0-এর আগে চারটি স্পেস (ফাঁকা জায়গা) দিয়ে নিয়েছি। এটিকে বলে ইনডেন্টেশন (Indentation)।
এরকম না করলেও প্রোগ্রামটি চলত এবং তাই অনেকেই ইন্ডেন্টেশনের ব্যাপারটি
গুরুত্ব দেয় না এবং ঠিকমতো ইনডেন্টেশন করে না। যেকোনো ভালো অভ্যাসের মতো
ইন্ডেন্টেশনের অভ্যাস তৈরি করাটা একটু কঠিন, তবে
বিষয়টা কিন্তু দাঁত মাজার মতোই গুরুত্বপূর্ণ। ইনডেন্টেশন করার অভ্যাস
ঠিকমতো তৈরি না হলে প্রোগ্রামারদের সহকর্মী বা বসের বকা শুনতে হয়, অনেক
জায়গায় তো ইন্টারভিউতেই বাদ পড়ে যেতে হয়। আশা করছি তোমরা ব্যাপারটি
বেশ গুরুত্ব সহকারে নেবে। আমি বইয়ের সমস্ত উদাহরণেই যথাযথভাবে ইনডেন্টেশন
করার চেষ্টা করব তবে ছাপার সময় একটু এদিক-ওদিক হতে পারে, সেটি
তোমরা বুঝে নেবে। ইন্ডেন্টশনের জন্য সাধারণত চারটি স্পেস দেওয়াটাই এখন
স্ট্যান্ডার্ড। তোমরা এডিটরে অপশন সেট করতে পারো যাতে ট্যাব (Tab) চাপলে সেটি চারটি স্পেসের সমান হয়। Codeblocks-এ Settings মেনুতে Editor-এ ক্লিক করে TAB Options-এ TAB indents চেক করো এবং TAB size in spaces 4 দাও।
এবারে তোমাদের জন্য একটি কাজ। একটি প্রোগ্রাম লেখো যেটি স্ক্রিনে প্রিন্ট করবে: I love my country, Bangladesh।
ডাটা টাইপ, ইনপুট ও আউটপুট-
এ অধ্যায়ে আমরা কিছু ছোট ছোট প্রোগ্রাম লিখব। সবগুলো প্রোগ্রাম অবশ্যই
কম্পিউটারে চালিয়ে দেখবে এবং একটু পরিবর্তন করে কম্পাইল ও রান করার চেষ্টা
করবে।
আমাদের প্রথম প্রোগ্রামটি হবে দুটি সংখ্যা যোগ করার প্রোগ্রাম। এখন কথা
হচ্ছে, সংখ্যাগুলো তো কম্পিউটারের মেমোরিতে রাখতে হবে, সেই জটিল কাজটি
কীভাবে করব? চিন্তা নেই! সব প্রোগ্রামিং ল্যাঙ্গুয়েজে ভেরিয়েবল বলে একটি
জিনিস আছে যেটি কোন নির্দিষ্ট মান ধারণ করার জন্য ব্যবহার করা হয়।
ভেরিয়েবলের একটি নাম দিতে হয়, তারপর ভেরিয়েবল = কোনো মান লিখে দিলে
ভেরিয়েবলের ভেতর সেটি ঢুকে যায়। এটির সঙ্গে গাণিতিক সমীকরণের কিন্তু কোনো
সম্পর্ক নেই। চলো, প্রোগ্রামটি লিখে রান করাই, তারপর ব্যাখ্যা করা যাবে।
#include <stdio.h>
int main()
{
int a;
int b;
int sum;
a = 50;
b = 60;
sum = a + b;
printf("Sum is %d", sum);
return 0;
}
প্রোগ্রাম: ২.১
প্রোগ্রামটি রান করাও, তুমি স্ক্রিনে দেখবে: Sum is 110।
এখানে a, b, sum তিনটি ভেরিয়েবল (variable) আলাদা সংখ্যা ধারণ করে। প্রথমে
আমাদের বলে দিতে হবে যে a, b, sum নামে তিনটি ভেরিয়েবল আছে। এবং এগুলোতে
কী ধরনের ডাটা থাকবে সেটিও বলে দিতে হবে। int a; দিয়ে আমরা কম্পাইলারকে
বলছি a নামে একটি ভেরিয়েবল এই প্রোগ্রামে আছে যেটি একটি পূর্ণসংখ্যা
(integer)-এর মান ধারণ করার জন্য ব্যবহার করা হবে। এই কাজটিকে বলে
ভেরিয়েবল ডিক্লারেশন। আর int হচ্ছে ডাটা টাইপ, যেটি দেখে সি-এর কম্পাইলার
বুঝবে যে এতে ইন্টিজার টাইপ ডাটা থাকবে। আরও বেশ কিছু ডাটা টাইপ আছে,
সেগুলো আমরা আস্তে আস্তে দেখব। আমরা চাইলে একই টাইপের ভেরিয়েবলগুলো
ডিক্লেয়ার করার সময় আলাদা লাইনে না লিখে একসঙ্গে কমা দিয়েও লিখতে
পারতাম, যেমন: int a, b, sum;। আর লক্ষ করো যে ভেরিয়েবল ডিক্লারেশনের শেষে
সেমিকোলন ব্যবহার করতে হয়।
এরপর আমি দুটি স্টেটমেন্ট লিখেছি:
a = 50;
b = 60;
এখানে a-এর মান 50 আর b-এর মান 60 বলে দিলাম (assign করলাম), যতক্ষণ না এটি
আমরা পরিবর্তন করছি, কম্পাইলার a-এর মান 50 আর b-এর মান 60 ধরবে।
পরের স্টেটমেন্ট হচ্ছে: sum = a + b;। এতে বোঝায়, sum-এর মান হবে a + b-এর
সমান, অর্থাৎ a ও b-এর যোগফল যে সংখ্যাটি হবে সেটি আমরা sum নামের
ভেরিয়েবলে রেখে দিলাম (বা assign করলাম)।
এবারে যোগফলটি মনিটরে দেখাতে হবে, তাই আমরা printf ফাংশন ব্যবহার করব।
printf("Sum is %d", sum);
এখানে দেখো printf ফাংশনের ভেতরে দুটি অংশ। প্রথম অংশে "Sum is %d" দিয়ে
বোঝানো হয়েছে স্ক্রিনে প্রিন্ট করতে হবে Sum is এবং তার পরে একটি ইন্টিজার
ভেরিয়েবলের মান যেটি %d-এর জায়গায় বসবে। তারপর কমা দিয়ে আমরা sum লিখে
বুঝিয়ে দিয়েছি যে %d-তে sum-এর মান প্রিন্ট করতে হবে। ইন্টিজারের জন্য
যেমন %d ব্যবহার করলাম, অন্য ধরনের ভেরিয়েবলের জন্য অন্য কিছু লিখতে হবে,
যেটি আমরা ব্যবহার করতে করতে শিখব। খুব ভালো হতো যদি আমি এখন একটি চার্ট
লিখে দিতাম যে সি ল্যাঙ্গুয়েজে কী কী ডাটা টাইপ আছে, সেগুলো কী দিয়ে লেখে
এবং প্রিন্ট করার জন্য কী করতে হবে আর তোমরা সেই চার্ট মুখস্থ করে ফেলতে।
কিন্তু শুধু শুধু মুখস্থ করার কোনো দরকার নেই, মুখস্থ করার প্রবণতা
চিন্তাশক্তি কমায় আর প্রোগ্রামারদের জন্য চিন্তা করার ক্ষমতা খুবই
গুরুত্বপূর্ণ।
আমরা ওপরের প্রোগ্রামটি চাইলে এভাবেও লিখতে পারতাম:
#include <stdio.h>
int main()
{
int a, b, sum;
a = 50;
b = 60;
sum = a + b;
printf("Sum is %d", sum);
return 0;
}
প্রোগ্রাম: ২.২
আবার ভেরিয়েবল ডিক্লেয়ার করার সময় একই সঙ্গে অ্যাসাইনও করা যায়:
#include <stdio.h>
int main()
{
int a = 50, b = 60, sum;
sum = a + b;
printf("Sum is %d", sum);
return 0;
}
প্রোগ্রাম: ২.৩
এখন তোমাদের জন্য একটি প্রশ্ন। নিচের প্রোগ্রামটির আউটপুট কী হবে?
#include <stdio.h>
int main()
{
int x, y;
x = 1;
y = x;
x = 2;
printf("%d", y);
return 0;
}
প্রোগ্রাম: ২.৪
কী মনে হয়? আউটপুট 1 নাকি 2? আউটপুট হবে 1, কারণ প্রথমে কম্পাইলার দেখবে,
x-এর মান 1 অ্যাসাইন করা হয়েছে (x = 1;)। তারপর x-এর মানটি আবার y-এ
অ্যাসাইন করা হয়েছে (y = x;)। এখন y-এর মান 1। তারপর আবার x-এর মান 2
অ্যাসাইন করা হয়েছে। কিন্তু তাতে y-এর মানের কোনো পরিবর্তন হবে না।
প্রোগ্রামিংয়ে y = x; আসলে কোনো সমীকরণ না যে এটি সবসময় সত্য হবে। '='
চিহ্ন দিয়ে একটি ভেরিয়েবলে নির্দিষ্ট কোনো মান রাখা হয়।
এবারে নিচের প্রোগ্রামটি দেখো:
#include <stdio.h>
int main()
{
int a = 50, b = 60, sum;
sum = a + b;
printf("%d + %d = %d", a, b, sum);
return 0;
}
প্রোগ্রাম: ২.৫
প্রোগ্রামটি মনিটরে কী প্রিন্ট করে? চালিয়ে দেখো। printf("%d + %d = %d",
a, b, sum); না লিখে printf("%d + %d = %d", b, a, sum); লিখে প্রোগ্রামটি
আবার চালাও। এখন জিনিসটি চিন্তা করে বুঝে নাও।
লেখাপড়া করার সময় আমাদের মনে নানা বিষয়ে নানা প্রশ্ন আসে, যার উত্তর
আমরা বইতে খুঁজি, শিক্ষককে জিজ্ঞাসা করি, ইন্টারনেটে খুঁজি বা চিন্তা করে
যুক্তি দাঁড় করিয়ে উত্তরটি বের করি। আমাদের দুর্ভাগ্য যে বেশিরভাগ
ছেলেমেয়েই শেষ কাজটি করে না, কারণ নিজে নিজে চিন্তা করতে একটু সময় লাগে ও
পরিশ্রম হয়, সেই সময় আর শ্রম তারা দিতে চায় না। আর আমাদের অভিভাবক,
শিক্ষক ও শিক্ষাব্যবস্থা চিন্তা করার জন্য কোনো পুরস্কার দেয় না, বরং
মুখস্থ করার জন্যই পুরস্কৃত করে।
যা-হোক, প্রোগ্রামিংয়ের ব্যাপারে যখনই মনে কোনো প্রশ্ন আসবে, সঙ্গে সঙ্গে
একটি প্রোগ্রাম লিখে ফেলবে। দেখো তোমার কম্পাইলার কী বলে। ধরা যাক, আমরা
যদি int টাইপের ভেরিয়েবলে দশমিক যুক্ত সংখ্যা (বাস্তব সংখ্যা বা real
number) ব্যবহার করতাম, তাহলে কী হতো?
#include <stdio.h>
int main()
{
int a = 50.45, b = 60, sum;
sum = a + b;
printf("%d + %d = %d", a, b, sum);
return 0;
}
প্রোগ্রাম: ২.৬
এখানে a-এর মান 50.45 ব্যবহার করলাম। এবারে প্রোগ্রাম চালাও, দেখো কী হয়।
আবার মনে যদি প্রশ্ন আসে যে, main ফাংশনের শেষ লাইনে return 0; না লিখলে কী
হয়? তাহলে return 0; ছাড়া প্রোগ্রাম চালিয়ে দেখো কী হয়।
আউটপুট হবে: 50 + 60 = 110।
সি কম্পাইলার a-এর মান 50 ধরেছে, যদিও আমরা 50.45 অ্যাসাইন করেছি। এই
ব্যাপারটিকে বলে টাইপ কাস্ট (type cast)। বাস্তব সংখ্যা রাখার জন্য সি
ভাষায় double নামের ডাটা টাইপ রয়েছে। টাইপ কাস্ট করে double সংখ্যাটিকে
int-এ নেওয়া হয়েছে, এটি অটোমেটিক হয়। আবার কম্পাইলারকে বলেও দেওয়া
যায়: int a = (int) 50.45।
int a = 50.99; এখানে a-এর মান হবে 50। int a = -50.9; লিখলে a-এর মান হয়
-50। এক কথায় বললে double থেকে int-এ টাইপ কাস্ট করলে দশমিকের পরের অংশটি
বাদ পড়ে যাবে।
আরেকটি কথা। যেই ভেরিয়েবলকে টাইপ কাস্ট করা হচ্ছে, তার মান কিন্তু
পরিবর্তন হয় না। টাইপ কাস্ট করা মানটি একটি ভেরিয়েবলে রাখা যায়। নিচের
প্রোগ্রামটি কম্পিউটারে চালালেই বুঝতে পারবে।
#include <stdio.h>
int main()
{
int n;
double x;
x = 10.5;
n = (int)x;
printf("Value of n is %d\n", n);
printf("Value of x is %lf\n", x);
return 0;
}
প্রোগ্রাম: ২.৭
প্রোগ্রামের আউটপুট দেখো। x-এর মান কিন্তু পরিবর্তন হয়নি। আর বুঝতেই পারছ
যে বাস্তব সংখ্যা রাখার জন্য সি-তে যে double টাইপের ভেরিয়েবল ব্যবহার করা
হয়, তা প্রিন্ট করার সময় %lf (l এখানে ইংরেজি ছোট হাতের L) ব্যবহার করতে
হয়।
int ডাটা টাইপে তো কেবল পূর্ণ সংখ্যা রাখা যায়। কিন্তু সেটি কী যেকোনো পূর্ণসংখ্যা? উত্তরের জন্য একটি প্রোগ্রাম লিখি:
#include <stdio.h>
int main()
{
int a;
a = 1000;
printf("Value of a is %d", a);
a = -21000;
printf("Value of a is %d", a);
a = 10000000;
printf("Value of a is %d", a);
a = -10000000;
printf("Value of a is %d", a);
a = 100020004000503;
printf("Value of a is %d", a);
a = -4325987632;
printf("Value of a is %d", a);
return 0;
}
প্রোগ্রাম: ২.৮
এখানে আমরা a-তে বিভিন্ন সংখ্যা অ্যাসাইন করলাম। সব মান কি ঠিকঠাক আসছে?
আসেনি। কেন আসেনি সেটি ব্যাখ্যা করার আগে একটি কথা বলে নিই। পরপর এতগুলো
printf-এর কারণে তোমার কম্পিউটারের স্ক্রিনে নিশ্চয়ই দেখতে একটু
অস্বস্তিকর লাগছে। প্রতিটি printf তোমরা এভাবে লিখতে পারো: printf("Value
of a is %d\n", a);। এখন printf ফাংশনে ""-এর ভেতর \n লিখলে কী হয় সেটি
আমি বলব না। প্রোগ্রামটি চালালেই বুঝতে পারবে।
a-এর সবগুলো মান কিন্তু ঠিকভাবে দেখা যায়নি, যেসব মান -2147483648 থেকে
2147483647 পর্যন্ত কেবল সেগুলোই ঠিকঠাক প্রিন্ট হবে, কারণ এই রেঞ্জের
বাইরের সংখ্যা int টাইপের ভেরিয়েবলে রাখা যায় না। এটি হলো int টাইপের
সংখ্যার সীমা। সি-তে int টাইপের ডাটার জন্য মেমোরিতে চার বাইট (byte)
জায়গা ব্যবহৃত হয়। চার বাইট মানে বত্রিশ বিট (1 byte = 8 bit)। প্রতি
বিটে দুটি জিনিস রাখা যায়, 0 আর 1। দুই বিটে রাখা যায় চারটি সংখ্যা (00,
01, 10, 11)। তাহলে 32 বিটে রাখা যাবে: 2^32 টা সংখ্যা অর্থাৎ 4294967296টি
সংখ্যা। এখন অর্ধেক ধনাত্মক আর অর্ধেক ঋণাত্মক যদি রাখি, তাহলে
-2147483648 থেকে -1 পর্যন্ত মোট 2147483648 সংখ্যা আবার 0 থেকে 2147483647
পর্যন্ত মোট 2147483648টি সংখ্যা, সর্বমোট 4294967296টি সংখ্যা। আশা
করি, হিসাবটা বুঝতে পেরেছ।
এখন আমরা যোগ করার প্রোগ্রামটি লিখব যেটি সব বাস্তব সংখ্যা (real number)
যোগ করতে পারবে। তোমাদের মনে করিয়ে দিই, পূর্ণসংখ্যা হচ্ছে, ... -3, -2,
-1, 0, 1, 2, 3 ... ইত্যাদি। আর বাস্তব সংখ্যা হচ্ছে -5, -3, -2.43, 0,
0.49, 2.92 ইত্যাদি (সংখ্যারেখার ওপর সব সংখ্যাই কিন্তু বাস্তব সংখ্যা)।
#include <stdio.h>
int main()
{
double a, b, sum;
a = 9.5;
b = 8.743;
sum = a + b;
printf("Sum is: %lf\n", sum);
printf("Sum is: %0.2lf\n", sum);
return 0;
}
প্রোগ্রাম: ২.৯
প্রোগ্রামটি কম্পাইল এবং রান করো। আউটপুট হবে নিচের দুই লাইন:
Sum is: 18.243000
Sum is: 18.24
%lf ব্যবহার করায় প্রথম লাইনে দশমিকের পরে ছয় ঘর পর্যন্ত প্রিন্ট হয়েছে।
আবার দ্বিতীয় লাইনে দশমিকের পরে দুই ঘর পর্যন্ত প্রিন্ট হয়েছে, কারণ
%0.2lf লিখেছি (তিন ঘর পর্যন্ত প্রিন্ট করতে চাইলে %0.3lf লিখতাম, আবার
দশমিক অংশ প্রিন্ট করতে না চাইলে %0.0lf)। double টাইপের ডাটার জন্য 64 বিট
ব্যবহৃত হয় এবং 1.7E-308 (1.7 x 10-308) থেকে 1.7E+308 (1.7 x 10308)
পর্যন্ত ডাটা রাখা যায়। বিস্তারিত হিসাব বুঝতে হলে কম্পিউটার
বিজ্ঞানসংক্রান্ত আরও কিছু জ্ঞানবুদ্ধির দরকার, তাই আমি আর এখন সেদিকে
যাচ্ছি না।
এখন আমরা আমাদের প্রোগ্রামে এমন ব্যবস্থা রাখতে চাই, যাতে কোন দুটি সংখ্যা
যোগ করতে হবে সেটি আমরা কোডের ভেতর লিখব না, ব্যবহারকারীর কাছ থেকে ইনপুট
আকারে জেনে নেব। ব্যবহারকারীর (মানে যে প্রোগ্রামটি ব্যবহার করছে) কাছ থেকে
ইনপুট নেওয়ার জন্য আমরা scanf ফাংশন ব্যবহার করব (সি-তে আরও ফাংশন আছে এই
কাজের জন্য)। তাহলে দেরি না করে প্রোগ্রাম লিখে ফেলি:
#include <stdio.h>
int main()
{
int a, b, sum;
scanf("%d", &a);
scanf("%d", &b);
sum = a + b;
printf("Sum is: %d\n", sum);
return 0;
}
প্রোগ্রাম: ২.১০
প্রোগ্রামটি রান করলে দেখবে ফাঁকা স্ক্রিন (blank screen) আসে। তখন তুমি
একটি সংখ্যা লিখবে, তারপর স্পেস (space) বা এন্টার (enter) দিয়ে আরেকটি
সংখ্যা লিখবে। তারপর আবার এন্টার চাপলে যোগফল দেখতে পাবে।
তোমরা নিশ্চয়ই scanf ফাংশনের ব্যবহার শিখে ফেলেছ। scanf("%d", &a);
এখানে ডবল কোটেশনের ভেতরে %d দিয়ে scanf-কে বলে দেওয়া হচ্ছে যে একটি
ইন্টিজার বা int টাইপের ভেরিয়েবলের মান পড়তে হবে (ব্যবহারকারী কিবোর্ড
থেকে ইনপুট দেবে)। আর দেখো a-এর আগে এমপারসেন্ড (&) চিহ্ন ব্যবহার করা
হয়েছে, &a দিয়ে বোঝানো হয়েছে যে সংখ্যাটি ইনপুট দেওয়া হবে সেটি a
ভেরিয়েবলের মাঝে অ্যাসাইন হবে। তোমরা যখন সি আরেকটু ভালোভাবে শিখবে, তখন
&a-এর প্রকৃত অর্থ বুঝতে পারবে, আপাতত আমরা ব্যবহারের দিকেই মনোযোগ
দিই। a এবং b-এর মান একটি scanf ফাংশন দিয়েও নেওয়া যেত এভাবে: scanf("%d
%d", &a, &b);। ভেরিয়েবলের আগে & চিহ্ন না দিলে কী সমস্যা?
নিচের প্রোগ্রামটি রান করার চেষ্টা করো, কিছু একটি এরর পাবে। এই মুহূর্তে
এররটা ব্যাখ্যা করছি না, কারণ ব্যাখ্যাটা একটু জটিল আর এখন বোঝাতে গেলে
তোমরা ভুল বুঝতে পারো এবং পরে আমাকে গালমন্দ করবে।
#include <stdio.h>
int main()
{
int a, b, sum;
scanf("%d", &a);
scanf("%d", b);
sum = a + b;
printf("Sum is: %d\n", sum);
return 0;
}
প্রোগ্রাম: ২.১১
এখন আমরা যদি ইনপুট হিসেবে ইন্টিজার না নিয়ে ডবল টাইপের ডাটা নিতে চাইতাম
তাহলে কী করতে হতো? scanf-এ %d-এর বদলে %lf ব্যবহার করলেই চলত। তোমরা
প্রোগ্রামটি লিখে ফেলো এবং দেখো ঠিকঠাক রান হয় কি না। তারপরে বাকি অংশ
পড়া শুরু করো।
আসলে ঠিকঠাক রান হবে না, কারণ ডাটা টাইপও পরিবর্তন করতে হবে। মানে int না লিখে double লিখতে হবে। প্রোগ্রামটি ঠিক করে আবার চালাও।
বইতে যখনই আমি কোনো প্রোগ্রাম লেখতে বলব সেটি যত সহজ কিংবা কঠিনই মনে হোক
না কেন, সেটি কম্পিউটারে লিখে কম্পাইল ও রান করতে হবে। এ কাজ না করে সামনে
আগানো যাবে না। মনে রাখবে, গাড়ি চালানো শেখার জন্য যেমন গাড়ি চালানোর
কোনো বিকল্প নেই, সাঁতার শেখার জন্য যেমন সাঁতার কাটার বিকল্প নেই, তেমনই
প্রোগ্রামিং শেখার জন্য প্রোগ্রামিং করার কোনো বিকল্প নেই, শুধু বই পড়ে
প্রোগ্রামার হওয়া যায় না।
এবারে আমরা আরেক ধরনের ডাটা টাইপ দেখব, সেটি হচ্ছে char (character) টাইপ।
তো এই character টাইপের চরিত্র হচ্ছে একে মেমোরিতে রাখার জন্য মাত্র এক
বাইট জায়গার দরকার হয়। সাধারণত যেকোনো অক্ষর বা চিহ্ন রাখার জন্য এই
টাইপের ডাটা ব্যবহার করা হয়। তবে সেই অক্ষরটা ইংরেজি বর্ণমালার অক্ষর হতে
হবে, অন্য ভাষার অক্ষর char টাইপের ভেরিয়েবলে রাখা যাবে না। নিচের
প্রোগ্রামটি কম্পিউটারে লিখে রান করাও:
#include <stdio.h>
int main()
{
char ch;
printf("Enter the first letter of your name: ");
scanf("%c", &ch);
printf("The first letter of your name is: %c\n", ch);
return 0;
}
প্রোগ্রাম: ২.১২
কোড দেখে বুঝতেই পারছ, char টাইপের জন্য printf এবং scanf ফাংশনের ভেতরে %c
ব্যবহার করতে হয়। আরেকটি ফাংশন আছে getchar, এটি দিয়েও char টাইপের ডাটা
রিড করা যায়। নিচের প্রোগ্রামটি দেখো:
#include <stdio.h>
int main()
{
char ch;
printf("Enter the first letter of your name: ");
ch = getchar();
printf("The first letter of your name is: %c\n", ch);
return 0;
}
প্রোগ্রাম: ২.১৩
এটি রান করাও। আগের প্রোগ্রামটির মতো একই কাজ করবে। getchar ফাংশন একটি
অক্ষর পড়ে সেটি ch ভেরিয়েবলের ভেতরে অ্যাসাইন করে দিল। আর সরাসরি কোনো
কিছু char টাইপ ভেরিয়েবলে রাখতে চাইলে যেই অক্ষর বা চিহ্ন রাখবে তার দুই
পাশে সিঙ্গেল কোটেশন চিহ্ন দেবে। যেমন: char c = 'A';
এখন নিচের প্রোগ্রামটি দেখো:
#include <stdio.h>
int main()
{
int num1, num2;
printf("Please enter a number: ");
scanf("%d", &num1);
printf("Please enter another number: ");
scanf("%d", &num2);
printf("%d + %d = %d\n", num1, num2, num1+num2);
printf("%d - %d = %d\n", num1, num2, num1-num2);
printf("%d * %d = %d\n", num1, num2, num1*num2);
printf("%d / %d = %d\n", num1, num2, num1/num2);
return 0;
}
প্রোগ্রাম: ২.১৪
এটি কম্পাইল ও রান করাও। এটি দেখে নিশ্চয়ই বুঝতে পারছ বিয়োগ, গুণ ও ভাগের
কাজ কীভাবে করতে হয়। এবারে তোমাদের কাজ হচ্ছে চারটি। এক, num1 ও num2-এর
মধ্যেকার যোগ, বিয়োগ, গুণ, ভাগের কাজটি printf ফাংশনের ভেতরে না করে আগে
করা এবং মানটি অন্য একটি ভেরিয়েবলে রেখে দেওয়া। এর জন্য একটি প্রোগ্রাম
লিখে ফেলো। দ্বিতীয় কাজ হচ্ছে প্রোগ্রামটি ডবল টাইপের ভেরিয়েবল ব্যবহার
করে করো। তৃতীয় কাজ হচ্ছে, num2-এর মান 0 দিয়ে দেখো কী হয়। চতুর্থ কাজটি
হচ্ছে printf ফাংশনের ভেতরে ডবল কোটেশনের ভেতরে যেই +, -, *, / চিহ্ন আছে
সেগুলো সরাসরি ব্যবহার না করে একটি char টাইপ ভেরিয়েবলে রেখে তারপর
ব্যবহার করা। চারটি কাজ ঠিকমতো করার পরে নিচের প্রোগ্রামটি দেখো:
#include <stdio.h>
int main()
{
int num1, num2, value;
char sign;
printf("Please enter a number: ");
scanf("%d", &num1);
printf("Please enter another number: ");
scanf("%d", &num2);
value = num1 + num2;
sign = '+';
printf("%d %c %d = %d\n", num1, sign, num2, value);
value = num1 - num2;
sign = '-';
printf("%d %c %d = %d\n", num1, sign, num2, value);
value = num1 * num2;
sign = '*';
printf("%d %c %d = %d\n", num1, sign, num2, value);
value = num1 / num2;
sign = '/';
printf("%d %c %d = %d\n", num1, sign, num2, value);
return 0;
}
প্রোগ্রাম: ২.১৫
প্রোগ্রামটি দেখলেই বুঝতে পারবে কী কাজ করে। তবে শুধু দেখে বুঝলেই হবে না। কোড করে কম্পাইল ও রান করো।
সি ল্যাঙ্গুয়েজে আরও বেশ কিছু ডাটা টাইপ রয়েছে। ইনপুট ও আউটপুটের জন্যও
রয়েছে নানা পদ্ধতি, যা তোমরা আস্তে আস্তে শিখবে (সব হয়তো এ বইয়ে থাকবে
না, সি-এর অন্য বই পড়লে জানতে পারবে)। আপাতত যা শিখেছ, তা দিয়েই তোমরা
অনেক প্রোগ্রাম লিখে ফেলতে পারবে।
এখন একটি মজার এবং দরকারি জিনিস বলে রাখি। প্রোগ্রামের কোডের ভেতরে তুমি
নিজের ভাষাও ব্যবহার করতে পারো। এটিকে বলে কমেন্ট (comment) করা। কম্পাইলার
কমেন্টগুলোকে প্রোগ্রামের অংশ ধরবে না। এক লাইনের কমেন্ট হলে // চিহ্ন
দিয়ে কমেন্ট শুরু করতে পারো। আর একাধিক লাইন থাকলে /* দিয়ে শুরু এবং */
দিয়ে শেষ করতে হবে। নিচের প্রোগ্রামটি কিন্তু ঠিকঠাক কম্পাইল ও রান হবে।
#include <stdio.h>
int main()
{
// test program - comment 1
printf("Hello ");
/* We have printed Hello,
now we shall print World.
Note that this is a multi-line comment */
printf("World"); // printed world
return 0;
}
প্রোগ্রাম: ২.১৬
এবারে একটি প্রশ্ন, (যেটি সি-এর টেক্সট বইয়ে এই চ্যাপ্টারের শুরুতেই বলে
দিত), ভেরিয়েবলগুলোর নামকরণের নিয়মকানুন কী? ভেরিয়েবলের নাম এক বা
একাধিক অক্ষরের হতে পারে, অক্ষরগুলো হতে পারে a থেকে z, A থেকে Z, 0 থেকে 9
এবং _ (আন্ডারস্কোর বা আন্ডারবার)। তবে একাধিক অক্ষরের ক্ষেত্রে প্রথম
অক্ষরটা অঙ্ক (ডিজিট) হতে পারবে না। তুমি একটি প্রোগ্রামে int 7d; লিখে
দেখো কম্পাইলার কী বলে। আর ভেরিয়েবলের নামগুলো অর্থপূর্ণ হলে ভালো হয়।
যেমন, যোগফল রাখার জন্য ভেরিয়েবলের নাম sum হলেই ভালো, যদিও y নাম দিলেও
প্রোগ্রাম চলে। অর্থপূর্ণ নাম দিলে বুঝতে সুবিধা হয়, ভেরিয়েবলটা কী
উদ্দেশ্যে ব্যবহার করা হয়েছে।কন্ডিশনাল লজিক-
তোমরা অনেকেই হয়তো জানো যে 'চাচা চৌধুরীর বুদ্ধি কম্পিউটারের চেয়েও
প্রখর'! এটি শুনে প্রথম প্রথম চাচা চৌধুরীর ওপর ভক্তি-শ্রদ্ধা অনেক বেড়ে
গেলেও একটু চিন্তা করলেই তোমরা বুঝতে পারবে যে আসলে তোমাদের সবার বুদ্ধি
কম্পিউটারের চেয়ে প্রখর। আসলে কম্পিউটারের তো কোনো বুদ্ধিই নেই।
প্রোগ্রামাররা যেভাবে প্রোগ্রাম লিখে দেয় কম্পিউটার সেভাবে কাজ করে। এখন
আমরা প্রোগ্রামিংয়ের সহজ অথচ খুব গুরুত্বপূর্ণ একটি বিষয় শিখব। সেটি
হচ্ছে কন্ডিশনাল লজিক। কোন শর্তে কী করতে হবে সেটি প্রোগ্রাম লিখে
কম্পিউটারকে বোঝাতে হবে। কথা না বাড়িয়ে আমরা প্রোগ্রাম লেখা শুরু করে
দিই। তোমরা কিন্তু অবশ্যই প্রতিটি প্রোগ্রাম কম্পিউটারে চালিয়ে দেখবে।
#include <stdio.h>
int main()
{
int n;
n = 10;
if(n >= 0) {
printf("The number is positive\n");
}
else {
printf("The number is negative\n");
}
return 0;
}
প্রোগ্রাম: ৩.১
ওপরের প্রোগ্রামটির কাজ কী? n-এর বিভিন্ন মান (যেমন: 0, -10, -2, 5, 988
ইত্যাদি) বসিয়ে তোমরা প্রোগ্রামটি চালাও। দেখা যাচ্ছে যে এর কাজ হচ্ছে n
ধনাত্মক (positive) না ঋণাত্মক (negative) সেটি নির্ণয় করা। কোন সংখ্যা
ধনাত্মক হতে গেলে একটি শর্ত পূরণ করতে হয়। সেটি হচ্ছে তাকে শূন্যের সমান
বা তার চেয়ে বড় হতে হয়। তাহলে আমাদের লজিকটি দাঁড়াচ্ছে এ রকম যে, 'n
যদি শূন্যের সমান বা বড় হয়, তবে nধনাত্মক, না হলে n ঋণাত্মক'। এই
ব্যাপারটি সি ল্যাঙ্গুয়েজে প্রকাশ করতে হয় if এবং তার সঙ্গে else ব্যবহার
করে। if-এর ভেতর একটি শর্ত (কন্ডিশন) লিখে দিতে হয় যা সত্যি হলেই কেবল
তার ভেতরের অংশের কাজ হয় (মানে if-এর পর যে দ্বিতীয় বন্ধনী { } ব্যবহার
করা হয়েছে তার ভেতরের সব কাজ)। আর কন্ডিশনটা লিখতে হয় প্রথম বন্ধনীর
ভেতরে। if-এর ভেতরে যেই কন্ডিশনটা আছে সেটি যদি মিথ্যা হয়, তবে else-এর
ভেতরের (দ্বিতীয় বন্ধনীর ভেতরে) অংশের কাজ হয়। সব প্রোগ্রামিং
ল্যাঙ্গুয়েজেই এটি আছে, তবে লিখার ধরন হয়তো আলাদা।
এখন আমাদের দেখতে হবে, কন্ডিশনগুলো কীভাবে লিখতে হবে? তোমরা এতক্ষণে জেনে
গেছ যে 'বড় কিংবা সমান' এই তুলনা করার জন্য >= চিহ্ণ ব্যবহার করা হয়।
'ছোট কিংবা সমান'-এর জন্য ব্যবহার করে <= চিহ্ন। দুটি সংখ্যা একটি
আরেকটির সমান কি না সেটি পরীক্ষার জন্য ব্যবহার করে == চিহ্ন (লক্ষ করো
এখানে দুটি সমান চিহ্ন আছে। শুরুর দিকে অনেকেই সমান কি না পরীক্ষার জন্য
ভুল করে = (একটি সমান চিহ্ন যা দিয়ে আসলে কোনো ভেরিয়েবলে কোনোকিছু
অ্যাসাইন করা হয়) ব্যবহার করে বিপদে পড়ে যায়)। দুটি সংখ্যা পরস্পর অসমান
কি না, এটি পরীক্ষার জন্য != চিহ্ন ব্যবহার করে। আর ছোট কিংবা বড়
পরীক্ষার জন্য < আর > চিহ্ন ব্যবহার করতে হয়। আরও ব্যাপার-স্যাপার
আছে। একবারে সব না শিখে চলো আস্তে আস্তে প্রোগ্রাম লিখে শেখা যাক। এখানে
ইন্ডেন্টেশনের ব্যাপারটিও কিন্তু খেয়াল কোরো। if কিংবা else-এর ভেতরের
ব্লকের সব লাইন কিন্তু if বা else যেখানে শুরু, তার চার ঘর (চারটি স্পেস)
ডান থেকে শুরু হয়েছে।
আমরা ওপরের প্রোগ্রামটি এভাবেও লিখতে পারতাম:
#include <stdio.h>
int main()
{
int n;
n = 10;
if(n < 0) {
printf("The number is negative\n");
}
else {
printf("The number is positive\n");
}
return 0;
}
প্রোগ্রাম: ৩.২
এখানে আমরা প্রথমে পরীক্ষা করেছি যে n শূন্যের চেয়ে ছোট কি না। যদি ছোট
হয়, তবে n নেগেটিভ; আর সেটি না হলে (সেটি না হওয়া মানে n অবশ্যই শূন্যের
সমান বা বড়) n পজিটিভ।
তোমাদের মধ্যে যারা একটু খুঁতখুঁতে স্বভাবের, তারা নিশ্চয়ই ভাবছ যে শূন্য
তো আসলে পজিটিভ বা নেগেটিভ কোনোটাই না। শূন্যের চেয়ে বড় সব সংখ্যা হচ্ছে
পজিটিভ আর ছোট সব সংখ্যা হচ্ছে নেগেটিভ। কম্পিউটারকে সেটি বোঝাতে গেলে আমরা
নিচের প্রোগ্রামটি লিখতে পারি:
#include <stdio.h>
int main()
{
int n = 10;
if(n < 0) {
printf("The number is negative\n");
}
else if (n > 0) {
printf("The number is positive\n");
}
else if (n == 0) {
printf("The number is zero!\n");
}
return 0;
}
প্রোগ্রাম: ৩.৩
প্রোগ্রামটি একটু ব্যাখ্যা করা যাক:
if(n < 0): এখানে আমরা প্রথমে পরীক্ষা করেছি n শূন্যের চেয়ে ছোট কি না ।
ছোট হলে তো কথাই নেই। আমরা বলে দিচ্ছি যে সংখ্যাটি নেগেটিভ। else if(n
> 0): আর যদি ছোট না হয়, তবে n শূন্যের চেয়ে বড় কি না সেটি পরীক্ষা
করেছি if(n > 0)। এই শর্ত সত্যি হলে সংখ্যাটি পজিটিভ।
else if(n == 0): আর n > 0ও যদি সত্যি না হয় তবে কোন শর্তটি বাদ রইল?
দুটি সমান কি না সেটি পরীক্ষা করা। তাহলে আমরা পরীক্ষা করছি যে n শূন্যের
সমান কি না এবং সমান হলে বলে দিচ্ছি যে এটি শূন্য।
দুটি সংখ্যা তুলনা করার সময় প্রথমটা যদি দ্বিতীয়টির চেয়ে বড় না হয়,
ছোটও না হয়, তবে তারা অবশ্যই সমান। তাই তৃতীয় কন্ডিশনটা আসলে আমাদের
দরকার নেই। আমরা প্রথম দুটি কন্ডিশন মিথ্যা হলেই বলে দিতে পারি যে n-এর মান
শূন্য।
#include <stdio.h>
int main()
{
int n = 10;
if(n < 0) {
printf("The number is negative\n");
}
else if (n > 0) {
printf("The number is positive\n");
}
else {
printf("The number is zero!\n");
}
return 0;
}
প্রোগ্রাম: ৩.৪
আবার সব সময় যে if ব্যবহার করলেই সঙ্গে else কিংবা else if ব্যবহার করতে হবে, এমন কোনো কথা নেই। নিচের প্রোগ্রামটি দেখো:
#include <stdio.h>
int main()
{
int number = 12;
if(number > 10) {
printf("The number is greater than ten\n");
}
return 0;
}
প্রোগ্রাম: ৩.৫
এখানে কেবল দেখা হচ্ছে যে সংখ্যাটির মান কি দশের চেয়ে বড় কি না।
নিচের প্রোগ্রামটি দেখে বলো তো আউটপুট কী হবে?
#include <stdio.h>
int main()
{
int n = 10;
if (n < 30) {
printf("n is less than 30.\n");
}
else if(n < 50) {
printf("n is less than 50.\n");
}
return 0;
}
প্রোগ্রাম: ৩.৬
আউটপুট হবে: n is less than 30. যদিও else if(n < 50) এটিও সত্য কিন্তু
যেহেতু if (n < 30) সত্য হয়ে গেছে, তাই এর সঙ্গে বাকি যত else if কিংবা
else থাকবে, সেগুলো আর পরীক্ষা করা হবে না। এখন তোমরা নিশ্চয়ই নিচের
প্রোগ্রামটির আউটপুট বলতে পারবে।
#include <stdio.h>
int main()
{
int n = 10;
if (n < 30) {
printf("n is less than 30.\n");
}
if(n < 50) {
printf("n is less than 50.\n");
}
return 0;
}
প্রোগ্রাম: ৩.৭
এখন আমরা আরেকটি প্রোগ্রাম লিখব। কোনো সংখ্যা জোড় না বেজোড় সেটি নির্ণয়
করার প্রোগ্রাম। কোনো সংখ্যা জোড় কি না সেটি বোঝার উপায় হচ্ছে সংখ্যাটিকে
2দিয়ে ভাগ করা। যদি ভাগশেষ শূন্য হয়, তবে সংখ্যাটি জোড়; আর ভাগশেষ
শূন্য না হয়ে এক হলে সেটি বেজোড়। সি ল্যাঙ্গুয়েজে ভাগশেষ বের করার জন্য
মডুলাস অপারেটর (modulus operator) আছে, যেটাকে '%' চিহ্ন দিয়ে প্রকাশ করা
হয়। তাহলে আর চিন্তা নেই।
শুরুতে একটি সংখ্যা নেব: int number;
এবারে number-এর জন্য একটি মান ঠিক করি: number = 5;
এখন numberকে 2 দিয়ে ভাগ করলে যে ভাগশেষ পাব সেটি বের করি: remainder = number % 2;
এখন if-এর সাহায্যে remainder-এর মান পরীক্ষা করে আমরা সিদ্ধান্তে পৌঁছে
যেতে পারি। remainder-এর কেবল দুটি মানই সম্ভব– 0 আর 1। পুরো প্রোগ্রামটি
লিখে ফেলি:
#include <stdio.h>
int main()
{
int number, remainder;
number = 5;
remainder = number % 2;
if(remainder == 0) {
printf("The number is even\n");
}
else {
printf("The number is odd\n");
}
return 0;
}
প্রোগ্রাম: ৩.৮
প্রোগ্রামটি remainder ভেরিয়েবল ব্যবহার না করেও লেখা যায়:
#include <stdio.h>
int main()
{
int number = 9;
if(number % 2 == 0) {
printf("The number is even\n");
}
else {
printf("The number is odd\n");
}
return 0;
}
প্রোগ্রাম: ৩.৯
আচ্ছা, আমাদের যদি কেবল জোড় সংখ্যা নির্ণয় করতে হতো, তাহলে আমরা কী করতাম? else ব্লকটা বাদ দিয়ে দিতাম।
তোমাদের জন্য এখন একটি ছোট্ট পরীক্ষা। মডুলাস অপারেটর ব্যবহার না করে
ভাগশেষ বের করতে পারবে? একবার করে গুণ, ভাগ ও বিয়োগ (*, /, -) ব্যবহার করে
কিন্তু কাজটি করা যায়। তোমরা সেটি করার চেষ্টা করতে পারো।
এবার আরেকটি প্রোগ্রাম দেখা যাক। কোনো একটি অক্ষর ছোট হাতের (small letter
বা lower case letter) নাকি বড় হাতের (capital letter বা upper case
letter), সেটি বের করতে হবে। এর জন্য সবচেয়ে সহজ সমাধানটা হতে পারে এই রকম
যে আমরা একটি character টাইপের ভেরিয়েবলের ভেতরে অক্ষরটা রাখতে পারি।
তারপর একে একে সেটিকে 26টি lower case letter এবং 26টি upper case
letter-এর সঙ্গে তুলনা করে দেখতে পারি। যখনই মিলে যাবে, তখনই বলে দেওয়া
যায়, অক্ষরটা কোন ধরনের।
char ch = 'p';
if (ch == 'a')
{
printf("%c is lower case\n", ch);
}
else if (ch == 'A')
{
printf("%c is upper case\n", ch);
}
else if (ch == 'b')
{
printf("%c is lower case\n", ch);
}
else if (ch == 'B')
{
printf("%c is upper case\n", ch);
}
else if (ch == 'c')
{
printf("%c is lower case\n", ch);
}
else if (ch == 'C')
{
printf("%c is upper case\n", ch);
}
… এভাবে চলবে।
কিন্তু এই সমস্যার সমাধান করার জন্য এত কোড লিখার কোনো দরকার নেই। এটি সহজে
করার জন্য আমাদের জানতে হবে এন্ড অপারেটরের (AND operator) ব্যবহার। সি
ল্যাঙ্গুয়েজে একে '&&' চিহ্ন দিয়ে প্রকাশ করা হয়। নিচের কোডটি
দেখলেই তোমরা এর কাজ বুঝে যাবে।
#include <stdio.h>
int main()
{
char ch = 'W';
if(ch >= 'a' && ch <= 'z') {
printf("%c is lower case\n", ch);
}
if(ch >= 'A' && ch <= 'Z') {
printf("%c is upper case\n", ch);
}
return 0;
}
প্রোগ্রাম: ৩.১০
'&&'-এর বাঁ পাশে একটি কন্ডিশন এবং ডান পাশে একটি কন্ডিশন থাকবে,
এবং দুটি কন্ডিশন সত্য হলেই সম্পূর্ণ কন্ডিশনটা সত্য হবে। ch >= 'a'
&& ch <= 'z' এটি পুরোটা একটি কন্ডিশন। এখন &&-এর বাঁ
দিকে একটি কন্ডিশন আছে ch >= 'a' আর ডানদিকে আরেকটি কন্ডিশন ch <=
'z'। দুটি কন্ডিশনই যদি সত্য হয়, তবে পুরো কন্ডিশনটা সত্য হবে। এখন
কম্পিউটার প্রতিটি অক্ষর বোঝার জন্য যেই কোড ব্যবহার করে তাতে a-এর চেয়ে
b-এর মান এক বেশি, b-এর চেয়ে c-এর মান এক বেশি, c-এর চেয়ে d-এর মান এক
বেশি … এরকম। তাই কোনো অক্ষর lower case হলে সেটি অবশ্যই 'a'-এর সমান কিংবা
বড় হতে হবে। আবার সেটি 'z'-এর সমান কিংবা ছোট হতে হবে। একইভাবে A-এর
চেয়ে B-এর মান এক বেশি, B-এর চেয়ে C-এর মান এক বেশি … এরকম। তাই কোনো
ক্যারেক্টারের মান 'A' থেকে 'Z'-এর মধ্যে হলে আমরা বলতে পারি যে সেটি upper
case। 'A'-এর সমান কিংবা বড় হতে হবে এবং 'Z'-এর সমান কিংবা ছোট হতে হবে।
আরেকটি ব্যাপার। দ্বিতীয় if-এর আগে else ব্যবহার করা উচিত। তাহলে
কম্পাইলার প্রথম if-এর ভেতরের শর্ত সত্য হলে আর পরের if-এর কন্ডিশন পরীক্ষা
করবে না। তাতে সময় বাঁচবে।
#include <stdio.h>
int main()
{
char ch = 'k';
if(ch >= 'a' && ch <= 'z') {
printf("%c is lower case\n", ch);
}
else if(ch >= 'A' && ch <= 'Z') {
printf("%c is upper case\n", ch);
}
return 0;
}
প্রোগ্রাম: ৩.১১
আশা করি, তোমরা '&&'-এর ব্যবহার বুঝে গেছ।
এখন আরেকটি অপারেটরের ব্যবহার দেখব। সেটি হচ্ছে অর (OR)। একে প্রকাশ করা
হয় '||' চিহ্ন দিয়ে (পরপর দুটি |)। '&&'-এর ক্ষেত্রে যেমন দুই
পাশের শর্ত সত্য হলেই সম্পূর্ণ শর্ত সত্য হয়, '||'-এর ক্ষেত্রে যেকোনো এক
পাশের শর্ত সত্য হলেই সম্পূর্ণ শর্ত সত্য হয়।
নিচের প্রোগ্রামটির আউটপুট কী হবে? কোড দেখে বলতে না পারলে প্রোগ্রামটি চালাও।
#include <stdio.h>
int main()
{
int num = 5;
if(num >= 1 || num <= 10) {
printf("yes\n");
}
else {
printf("no\n");
}
return 0;
}
প্রোগ্রাম: ৩.১২
এটির আউটপুট হবে yes। এখন num-এর মান 50 করে দাও। আউটপুট কী হবে?
এবারেও আউটপুট yesই হবে। কারণ num-এর মান 50 হলে, প্রথম শর্তটি সত্য হবে
(num >= 1) আর দ্বিতীয় শর্তটি (n <= 10) মিথ্যা হবে। তবে আমরা
যেহেতু দুটি শর্তের মাঝে '||' ব্যবহার করেছি, তাই যেকোনো একটি শর্ত সত্য
হলেই সম্পূর্ণ শর্তটি সত্য হবে। এখন আরও একটি সমস্যা। কোনো অক্ষর vowel
নাকি consonant, সেটি নির্ণয় করতে হবে। আমরা জানি, vowelগুলো হচ্ছে a,
e, i, o, u। এখন কোনো ক্যারেক্টার এই পাঁচটির মধ্যে পড়ে কি না সেটি
নির্ণয় করার জন্য যদি আমরা এমন শর্ত দিই: ch >= 'a' && ch
<= 'u' তাহলে কিন্তু হবে না। কারণ তাহলে a থেকে u পর্যন্ত সব অক্ষরের
জন্যই শর্তটি সত্যি হবে কিন্তু আমাদের দরকার নির্দিষ্ট কিছু অক্ষর। তাই
শর্তটি আমরা এভাবে লিখতে পারি:
if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') {
printf("%c is vowel\n", ch);
}
else {
printf("%c is consonant\n", ch);
}
তাহলে এবার সম্পূর্ণ প্রোগ্রামটি তোমরা লিখে ফেলতে পারো।লুপ (Loop)-
তোমরা এরই মধ্যে প্রোগ্রামের মধ্যে বিভিন্ন ধরনের শর্ত (condition) ব্যবহার
করতে শিখে গেছ। এইসব শর্ত দিয়ে বিভিন্ন প্রোগ্রাম তৈরি করাও হয়তো শুরু
করে দিয়েছ। খুব ভালো কথা। কিন্তু এখন আমরা আরেকটি সমস্যা ও তার সমাধানের
পথ খুঁজব। একটি প্রোগ্রাম লিখতে হবে, যেটি 1 থেকে 10 পর্যন্ত সব
পূর্ণসংখ্যা মনিটরে দেখাবে (প্রতি লাইনে একটি সংখ্যা থাকবে)। খুবই সহজ
সমস্যা এবং সমাধানও অত্যন্ত সহজ। আমি জানি, তোমরা এক মিনিটের মধ্যেই নিচের
প্রোগ্রামটি লিখে ফেলবে:
#include <stdio.h>
int main()
{
printf("1\n");
printf("2\n");
printf("3\n");
printf("4\n");
printf("5\n");
printf("6\n");
printf("7\n");
printf("8\n");
printf("9\n");
printf("10\n");
return 0;
}
প্রোগ্রাম: ৪.১
এখানে আমরা 1 থেকে 10 পর্যন্ত সবগুলো সংখ্যা প্রিন্ট করে দিয়েছি। অবশ্য
একটি printf() ব্যবহার করেও কাজটি করা যেত:
printf("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
আবার প্রোগ্রামটি এভাবেও লেখা যেত। n একটি ইন্টিজার ভেরিয়েবল, যার মান
আমরা প্রথমে 1 বসাব। তারপর n-এর মান প্রিন্ট করব। তারপর n-এর মান এক বাড়াব
(n = n + 1 অথবা সংক্ষেপে, n++ লিখে)।
int n = 1;
printf("%d\n", n);
n = n + 1;
printf("%d\n", n);
n = n + 1;
printf("%d\n", n);
n = n + 1;
/* এভাবে মোট দশ বার */
আবার nএর মান 1 বাড়ানোর কাজটি কিন্তু এক লাইনেই সেরে ফেলা যায়।
printf("%d\n", n);
n = n + 1;
এর পরিবর্তে আমরা লিখতে পারি:
printf("%d\n", n++);
যা-ই হোক, এ তো গেল 1 থেকে 10 পর্যন্ত প্রিন্ট করা। কিন্তু আমাদের যদি 1
থেকে 100, বা 1000, বা 10000 পর্যন্ত প্রিন্ট করতে বলা হতো তাহলে আমরা কী
করতাম? ওপরে যে পদ্ধতি অবলম্বন করা হয়েছে সেটি তো অবশ্যই করা যেত। কিন্তু
আমি জানি, তোমরা কেউই এত কষ্ট করতে রাজি না।
এ সমস্যা সমাধানের জন্য সব প্রোগ্রামিং ল্যাঙ্গুয়েজেই লুপ (loop) বলে একটি
পদ্ধতি রয়েছে। এটি দিয়ে একই কাজ বারবার করা যায়। লুপের মধ্যে একটি শর্ত
বসিয়ে দিতে হয়, যেটি পূরণ না হওয়া পর্যন্ত প্রোগ্রামটি লুপের ভেতরের
কাজ বারবার করতে থাকবে। সি ল্যাঙ্গুয়েজে দুটি জনপ্রিয় লুপ হচ্ছে while
এবং for। আমরা এখন while ব্যবহার করে ওই প্রোগ্রামটি লিখব।
#include <stdio.h>
int main()
{
int n = 1;
while(n <= 10) {
printf("%d\n", n);
n++;
}
return 0;
}
প্রোগ্রাম: ৪.২
কী চমৎকার! এখন আমরা চাইলে 10-এর বদলে যত খুশি বসাতে পারি, যত বসাব 1 থেকে
তত পর্যন্ত প্রিন্ট হবে। while লুপে প্রথম বন্ধনীর ভেতর শর্ত লিখে দিতে
হয়। প্রোগ্রাম সেই শর্ত পরীক্ষা করে। যতক্ষণ পর্যন্ত শর্তটি সত্য হয়
ততক্ষণ পর্যন্ত লুপের ভেতরের কাজগুলো চলতে থাকে। লুপের ভেতরের কাজগুলো
থাকবে দ্বিতীয় বন্ধনীর ভেতর। যেমন এখানে লুপের ভেতরে আমরা দুটি কাজ করেছি।
n-এর মান প্রিন্ট করেছি আর তারপর n-এর মান 1 বাড়িয়েছি। n-এর মান 1 করে
বাড়তে থাকলে একসময় এটি 11 হবে আর তখন n <= 10 এই শর্তটি মিথ্যা হয়ে
যাবে (কারণ 11 > 10)। আর প্রোগ্রামটিও লুপ থেকে বের হয়ে আসবে। অর্থাৎ,
শর্তটি যখনই মিথ্যা হবে তখনই লুপ থেকে বের হয়ে যাবে।
ইন্ডেন্টেশনের ব্যাপারটিও খেয়াল করো। লুপের ভেতরের অংশের কোড চার ঘর ডানদিক থেকে শুরু হয়েছে।
এবারে তোমাদের জন্য একটি প্রশ্ন। বলো তো নিচের প্রোগ্রামটির আউটপুট কী হবে?
#include <stdio.h>
int main()
{
int n = 1;
while(n <= 10) {
printf("%d\n", n);
}
n++;
return 0;
}
প্রোগ্রাম: ৪.৩
এটাও কি 1 থেকে 10 পর্যন্ত সব সংখ্যা প্রিন্ট করবে? দেখা যাক। প্রোগ্রামটি রান করাও। আউটপুট কী?
মনিটরে প্রতি লাইনে 1 প্রিন্ট হচ্ছে এবং প্রোগ্রামটি বন্ধ হচ্ছে না। খুবই দুঃখের বিষয়। দেখা যাক দুঃখের পেছনে কারণটা কী।
int n = 1; প্রথমে প্রোগ্রামটি n-এর মান 1 বসাবে।
তারপর while লুপে গিয়ে শর্ত পরীক্ষা করবে। আমরা শর্ত দিয়েছি n <= 10
মানে n-এর মান 10-এর ছোট বা সমান। এই শর্ত তো সত্য কারণ n-এর মান 1। তারপর
প্রোগ্রামটি n-এর মান প্রিন্ট করবে printf("%d\n", n);। তারপর কি n-এর মান 1
বাড়বে? বাড়বে না, কারণ আমরা দ্বিতীয় বন্ধনী শেষ করে দিয়েছি '}' চিহ্ন
দিয়ে (মানে লুপ শেষ)। তার মানে প্রোগ্রামটি আবার শর্ত পরীক্ষা করবে, আবার
n-এর মান প্রিন্ট করবে...এভাবে চলতেই থাকবে কারণ n-এর মান যেহেতু বাড়ছে
না, n <= 10 শর্তটি সব সময় সত্যই রয়ে যাচ্ছে – কখনো মিথ্যা হচ্ছে না।
এখন তোমরা while লুপ নিয়ে বিভিন্ন ধরনের গবেষণা চালিয়ে যেতে পারো। সব
সময় সত্য হয় এমন শর্ত ব্যবহার করে তোমার কম্পিউটারকে ব্যস্ত রাখতে পারো।
while(1){...} এখানে শর্ত হিসেবে 1 ব্যবহার করা হয়েছে। কম্পিউটার 1 বলতে
বোঝে সত্য। সুতরাং লুপের ভেতরের কাজগুলো সব সময় চলতে থাকবে, বন্ধ হবে না।
while(1 == 1){...} ও একই আচরণ করবে। তবে এখন আমি তোমাদের একটি দরকারি
জিনিস বলে রাখি, যেটি দিয়ে তোমরা জোর করে লুপ থেকে বের হয়ে যেতে পারবে।
সেটি হচ্ছে break স্টেটমেন্ট। কথা না বাড়িয়ে একটি প্রোগ্রাম লিখলেই
ব্যাপারটি পরিষ্কার হয়ে যাবে।
#include <stdio.h>
int main()
{
int n = 1;
while(n <= 100) {
printf("%d\n", n);
n++;
if(n > 10) {
break;
}
}
return 0;
}
প্রোগ্রাম: ৪.৪
এই প্রোগ্রামটি কী করবে? 1 থেকে 10 পর্যন্ত প্রিন্ট করবে। যদিও while-এর
ভেতর আমরা বলেছি যে শর্ত হচ্ছে n <= 100, কিন্তু লুপের ভেতরে আবার বলে
দিয়েছি যে যদি n > 10 হয়, তবে break; মানে বের হয়ে যাও, বা লুপটি
ভেঙে দাও। break সব সময় যেই লুপের ভেতর থাকে সেটির বাইরে প্রোগ্রামটিকে
নিয়ে আসে। সুতরাং n-এর মান 10 প্রিন্ট হওয়ার পরে এর মান এক বাড়বে (n++;)
অর্থাৎ n-এর মান হবে 11। আর তখন n > 10 সত্য হবে, ফলে প্রোগ্রামটি if
কন্ডিশনের ভেতরে ঢুকে যাবে। সেখানে গিয়ে সে দেখবে তাকে break করতে বলা
হয়েছে তাই সে লুপের বাইরে চলে যাবে। break-এর উল্টা কাজ করে, এমন একটি
স্টেটমেন্ট হচ্ছে continue;। কোনো জায়গায় continue ব্যবহার করলে লুপের
ভেতরে continue-এর পরের অংশের কাজ আর হয় না। নিচের প্রোগ্রামটি কোড করে
কম্পাইল ও রান করো:
#include <stdio.h>
int main()
{
int n = 0;
while (n < 10) {
n = n + 1;
if (n % 2 == 0) {
continue;
}
printf("%d\n", n);
}
return 0;
}
প্রোগ্রাম: ৪.৫
এই প্রোগ্রামটি 1 থেকে 10-এর মধ্যে কেবল বেজোড় সংখ্যাগুলো প্রিন্ট করবে।
জোড় সংখ্যার বেলায় continue ব্যবহার করার কারণে প্রোগ্রামটি
printf("%d\n", n); স্টেটমেন্ট এক্সিকিউট না করে লুপের পরবর্তী ধাপের কাজ
শুরু করবে।
এবারে আমরা আরেকটি প্রোগ্রাম লিখব। ছোটবেলায় যে নামতাগুলো তোমরা শিখেছ
সেগুলো এখন আমরা প্রোগ্রাম লিখে কম্পিউটারের মনিটরে দেখব। চলো 5-এর নামতা
দিয়ে শুরু করা যাক। আমাদের প্রোগ্রামের আউটপুট হবে এরকম:
5 X 1 = 5
5 X 2 = 10
5 X 3 = 15
5 X 4 = 20
5 X 5 = 25
5 X 6 = 30
5 X 7 = 35
5 X 8 = 40
5 X 9 = 45
5 X 10 = 50
তোমরা নিশ্চয়ই এখন অনেকগুলো printf ফাংশন লেখা শুরু করবে না। লুপের সাহায্যে প্রোগ্রামটি লিখে ফেলবে:
#include <stdio.h>
int main()
{
int n = 5;
int i = 1;
while (i <= 10) {
printf("%d X %d = %d\n", n, i, n*i);
i = i + 1;
}
return 0;
}
প্রোগ্রাম: ৪.৬
এতক্ষণ আমরা while লুপ ব্যবহার করলাম। এবার চলো for লুপ ব্যবহার করতে শিখি।
5-এর নামতার প্রোগ্রামটি যদি আমরা for লুপ ব্যবহার করে লিখি তাহলে সেটির
চেহারা দাঁড়াবে:
#include <stdio.h>
int main()
{
int n = 5;
int i;
for(i = 1; i <= 10; i = i + 1) {
printf("%d X %d = %d\n", n, i, n*i);
}
return 0;
}
প্রোগ্রাম: ৪.৭
for লুপের প্রথম বন্ধনীর ভেতর তিনটি অংশ লক্ষ করো। প্রতিটি অংশ সেমিকোলন
(;) দিয়ে আলাদা করা হয়েছে। প্রোগ্রামটি যখন লুপের ভেতর ঢুকে তখন প্রথম
সেমিকোলনের আগে আমরা যে কাজগুলো করতে বলব, সেগুলো একবার করবে। যেমন এখানে
i-এর মান 1 বসাবে। তারপর দ্বিতীয় অংশের কাজ করবে। দ্বিতীয় অংশে সাধারণত
শর্ত ব্যবহার করা হয় (while লুপে প্রথম বন্ধনীর ভেতর আমরা যে কাজটি করি
আরকি)। ওপরের প্রোগ্রামে আমরা দ্বিতীয় অংশে i <= 10 শর্তটি ব্যবহার
করেছি। এই শর্ত যদি মিথ্যা হয় তবে প্রোগ্রামটি লুপ থেকে বেরিয়ে আসবে। আর
যদি সত্য হয় তবে লুপের ভেতরের কাজগুলো করবে এবং তার পর for লুপের সেই
প্রথম বন্ধনীর ভেতর তৃতীয় অংশে যে কাজগুলো করতে বলা হয়েছে সেগুলো করবে।
তারপর আবার দ্বিতীয় অংশে এসে শর্ত পরীক্ষা করবে। প্রথম অংশের কাজ কিন্তু
আর হবে না। তো আমাদের প্রোগ্রামটি আবার লক্ষ করো। i <= 10 সত্য, কারণ
i-এর মান 1। তারপর printf() ফাংশনের কাজ হবে। তারপর i = i + 1 স্টেটমেন্ট
এক্সিকিউট হবে (i-এর মান এক বেড়ে যাবে)। তারপর আবার i <= 10 সত্য না
মিথ্যা সেটি পরীক্ষা করা হবে (i-এর মান এখন 2)। তারপর আবার লুপের ভেতরের
কাজ হবে (printf())। এভাবে যতক্ষণ না i <= 10 শর্তটি মিথ্যা হচ্ছে
ততক্ষণ লুপের ভেতরের কাজ চলতে থাকবে। i-এর মান এক এক করে বেড়ে বেড়ে যখন
11 হবে তখন শর্তটি মিথ্যা হবে আর প্রোগ্রামটি লুপ থেকে বের হয়ে আসবে। for
লুপের প্রথম বন্ধনীর ভেতরের তিনটি অংশই যে ব্যবহার করতে হবে এমন কোন কথা
নেই। কোন অংশ ব্যবহার করতে না চাইলে আমরা সেটি ফাঁকা রেখে দিতে পারি, তবে
সেমিকোলন কিন্তু অবশ্যই দিতে হবে। যেমন আমরা যদি i-এর মান আগেই নির্ধারণ
করে দেই তবে সেটি লুপের ভেতর না করলেও চলে।
int i = 1;
for(; i<= 10; i = i + 1) {
printf("%d X %d = %d\n", n, i, n*i);
}
যদি তিনটি অংশের কোনোটিই লিখতে না চাই, তবে পুরো প্রোগ্রামটি এভাবে লেখা যায়:
#include <stdio.h>
int main()
{
int n = 5;
int i = 1;
for( ; ; ) {
printf("%d X %d = %d\n", n, i, n*i);
i = i + 1;
if (i > 10) {
break;
}
}
return 0;
}
প্রোগ্রাম: ৪.৮
এখন আমরা আরেকটি কাজ করব। for লুপ ব্যবহার করে 5-এর নামতায় যে গুণ করেছি
(n*i) সেটি না করে কেবল যোগ করে প্রোগ্রামটি লিখব। তোমরা কি অবাক হচ্ছ যে
নামতার প্রোগ্রাম আবার গুণ ছাড়া কীভাবে হবে? আমরা কিন্তু 5 x 3-কে লিখতে
পারি 5 + 5 + 5। আমি কী করতে যাচ্ছি তা বুঝতে পারছ নিশ্চয়ই। প্রোগ্রামটি
লিখে ফেলি:
#include <stdio.h>
int main()
{
int m, n = 5;
int i;
m = 0;
for(i = 1; i <= 10; i = i + 1) {
m = m + n;
printf("%d X %d = %d\n", n, i, m);
}
return 0;
}
প্রোগ্রাম: ৪.৯
প্রোগ্রামটিতে আমরা গুণ না করে যোগ করলাম। কম্পাইল ও রান করে দেখো। কাজ
করবে ঠিকঠাক। কোনো সংখ্যার গুণিতকগুলো যেমন গুণ করে বের করা যায়, তেমনই
যোগ করেও করা যায়। আমরা যদি কোনো প্রোগ্রামে দেখি যে গুণ না করে যোগ করলেই
কাজ হচ্ছে, তাহলে যোগ করাই ভালো কারণ কম্পিউটারের প্রসেসর একটি যোগ করতে
যে সময় নেয়, একটি গুণ করতে তার চেয়ে অনেক বেশি সময় নেয়। যদিও তুমি
হয়তো প্রোগ্রাম রান করার সময় তা বুঝতে পারো না। কম্পিউটারের প্রসেসর
সম্পর্কে বিস্তারিত লেখাপড়া করলে বিষয়টা জানতে পারবে। আপাতত এটি জানলেই
চলবে যে একটি গুণ করার চেয়ে একটি যোগ করা ভালো, কারণ যোগ করতে কম্পিউটার
অপেক্ষাকৃত কম সময় নেয়।
তো আমরা for লুপ শিখে ফেললাম। এখন আমরা চেষ্টা করব শুধু নির্দিষ্ট একটি
সংখ্যার নামতা না লিখে 1 থেকে 20 পর্যন্ত সবগুলো সংখ্যার নামতা একবারে লিখে
ফেলতে। অর্থাৎ n-এর মান 5 নির্দিষ্ট না করে 1 থেকে 20 পর্যন্ত হবে। এটি
করার একটি বোকা পদ্ধতি (নাকি চোরা পদ্ধতি?) হচ্ছে নামতা লিখার অংশটি বারবার
কপি-পেস্ট করা। কিন্তু আমরা এটি করব লুপের ভেতর লুপ ব্যবহার করে। একটি
লুপের সাহায্যে n-এর মান 1 থেকে 20 পর্যন্ত এক করে বাড়াব। আর তার ভেতর
n-এর একটি নির্দিষ্ট মানের জন্য নামতাটা লিখব।
#include <stdio.h>
int main()
{
int n, i;
for(n = 1; n <= 20; n = n + 1) {
for(i = 1; i <= 10; i = i + 1) {
printf("%d X %d = %d\n", n, i, n*i);
}
}
return 0;
}
প্রোগ্রাম: ৪.১০
এখন তোমরা প্রোগ্রামটি চালাও। তারপর তোমাদের কাজ হবে গুণ না করে কেবল যোগ ব্যবহার করে প্রোগ্রামটি লেখা।
আমরা এখানে একটি for লুপের ভেতর আরেকটি for লুপ, যাকে নেস্টেড লুপও (nested
loop) বলে, সেটি ব্যবহার করলাম। তো আমরা চাইলে for লুপের ভেতর for বা
while অথবা while লুপের ভেতর for বা while লুপ একাধিকবার ব্যবহার করতে
পারি। অবশ্য সেটি কখনোই চার বা পাঁচবারের বেশি দরকার হওয়ার কথা না।
নেস্টেড লুপ দিয়ে আমরা এখন আরেকটি প্রোগ্রাম লিখব। 1, 2, 3 – এই তিনটি
সংখ্যার সব বিন্যাস (permutation) বের করার প্রোগ্রাম। বিন্যাসগুলো ছোট
থেকে বড় ক্রমে দেখাতে হবে অর্থাৎ প্রোগ্রামটির আউটপুট হবে এই রকম:
1, 2, 3
1, 3, 2
2, 1, 3
2, 3, 1
3, 1, 2
3, 2, 1
এই প্রোগ্রামটি অনেকভাবে লেখা যেতে পারে, কিন্তু আমরা এখন পর্যন্ত যতটুকু
প্রোগ্রামিং শিখেছি, তাতে নেস্টেড লুপের ব্যবহারই সবচেয়ে ভালো সমাধান।
এখানে আমরা প্রথম সংখ্যাটির জন্য একটি লুপ, দ্বিতীয় সংখ্যাটির জন্য প্রথম
লুপের ভেতরে একটি লুপ এবং তৃতীয় সংখ্যাটির জন্য দ্বিতীয় লুপের ভেতর
আরেকটি লুপ ব্যবহার করব।
#include <stdio.h>
int main()
{
int a, b, c;
for (a = 1; a <= 3; a++) {
for (b = 1; b <= 3; b++) {
for (c = 1; c <= 3; c++) {
printf ("%d, %d, %d\n", a, b, c);
}
}
}
return 0;
}
প্রোগ্রাম: ৪.১১
এখন প্রোগ্রামটি রান করলে আমরা এই রকম আউটপুট পাব:
1, 1, 1
1, 1, 2
1, 1, 3
1, 2, 1
1, 2, 2
1, 2, 3
1, 3, 1
1, 3, 2
1, 3, 3
2, 1, 1
2, 1, 2
2, 1, 3
2, 2, 1
2, 2, 2
2, 2, 3
2, 3, 1
2, 3, 2
2, 3, 3
3, 1, 1
3, 1, 2
3, 1, 3
3, 2, 1
3, 2, 2
3, 2, 3
3, 3, 1
3, 3, 2
3, 3, 3
কিন্তু আমরা তো আসলে এই রকম জিনিস চাচ্ছি না। a-এর মান যখন 1 তখন b ও c-এর
মান 1 হবে না, আবার b এবং c-এর মানও সমান হবে না। মানে a, b ও c আলাদা হবে।
তাহলে আমরা লুপের ভেতর শর্তগুলো একটু পরিবর্তন করব। দ্বিতীয় লুপের শর্ত b
<= 3-এর সঙ্গে আরেকটি শর্ত জুড়ে দেব, b != a। b <= 3 && b
!= a মানে b-এর মান 3-এর চেয়ে ছোট বা সমান হবে এবং b-এর মান a-এর মানের
সমান হবে না। তৃতীয় লুপে আমরা এখন শর্ত দেব, c <= 3 && c != a
&& c != b, মানে c-এর মান 3-এর ছোট বা সমান হতে হবে এবং c-এর মান
a-এর মানের সমান হওয়া চলবে না এবং c-এর মান b-এর মানের সমান হলেও চলবে
না। তাহলে আমাদের প্রোগ্রামটির চেহারা দাঁড়াবে এই রকম:
#include <stdio.h>
int main()
{
int a, b, c;
for (a = 1; a <= 3; a++) {
for (b = 1; b <= 3 && b != a; b++) {
for (c = 1; c <= 3 && c != a && c != b; c++) {
printf ("%d, %d, %d\n", a, b, c);
}
}
}
return 0;
}
প্রোগ্রাম: ৪.১২
রান করলে আমরা আউটপুট কী দেখব?
3, 2, 1
মাত্র একটি লাইন! আমরা প্রোগ্রামটি ঠিক করতে গিয়ে ঝামেলা পাকিয়ে ফেলেছি
মনে হচ্ছে। তোমরা কি একটু চিন্তা করে ঝামেলার কারণ বের করতে পারবে?
প্রথমে a-এর মান 1তাই a <= 3 সত্য। প্রোগ্রামটি প্রথম লুপের ভেতর ঢুকে
গেল। তারপর দ্বিতীয় লুপের শুরুতে b-এর মান 1। b <= 3 সত্য। কিন্তু b !=
a মিথ্যা। কারণ aও b-এর মান তো সমান, দুটোর মানই 1। তাই প্রোগ্রামটি আর
দ্বিতীয় লুপের ভেতর ঢুকবে না। এরপর a-এর মান 1 বাড়ল (a++)। a <= 3
সত্য (a-এর মান 2)। এখন দ্বিতীয় লুপ শুরু হবে। b-এর মান 1। এবারে b <= 3
এবং b != a দুটি শর্তই সত্য। প্রোগ্রামটি দ্বিতীয় লুপের ভেতর ঢুকে যাবে।
তৃতীয় লুপের শুরুতে c-এর মান 1। c <=3 সত্য, c !=a সত্য কিন্তু c !=b
মিথ্যা (দুটোর মানই 1)। তাই প্রোগ্রামটি তৃতীয় লুপ থেকে বের হয়ে যাবে–
কেবল তিনটি শর্ত সত্য হলেই প্রোগ্রামটি তৃতীয় লুপের ভেতর ঢুকবে এবং a, b ও
c-এর মান প্রিন্ট করবে। এভাবে কিছুক্ষণ গবেষণা করলে তোমরা দেখবে যে যখন
a-এর মান 3, b-এর মান 2 এবং c-এর মান 1, তখনই কেবল সব শর্ত সত্য হয় আর
আমরা আউটপুট পাই: 3, 2, 1। আসলে দ্বিতীয় লুপে আমরা b-এর মান a-এর মানের
সমান হলে লুপ থেকে বের হয়ে যাচ্ছি। সেই কাজটি করা ঠিক হচ্ছে না। আমাদের
উচিত দুটো মান সমান হলে পরবর্তী মানের জন্য চেষ্টা করা। আর মান দুটো সমান
না হলেই কেবল পরবর্তী কাজ করা। তাহলে আমরা লিখতে পারি: for (b = 1; b
<= 3; b++) { if (b != a) { /* b-এর মান a-এর মানের সমান না হলেই
ভেতরের অংশে প্রোগ্রামটি ঢুকবে। */ for (c = 1; c <= 3; c++) {
if (c != a && c != b) { /*c-এর মান a-এর মানের সমান
না হলে এবং c-এর মান b-এর মানের সমান না হলেই কেবল ভেতরের অংশে প্রোগ্রামটি
ঢুকবে। */ printf ("%d, %d, %d\n", a, b, c);
} } } } তাহলে আমাদের পুরো প্রোগ্রামটি দাঁড়াচ্ছে এই রকম:
#include <stdio.h>
int main()
{
int a, b, c;
for (a = 1; a <= 3; a++) {
for (b = 1; b <= 3; b++) {
if (b != a) {
for (c = 1; c <= 3; c++) {
if (c != b && c != a){
printf ("%d, %d, %d\n", a, b, c);
}
}
}
}
}
return 0;
}
প্রোগ্রাম: ৪.১৩
প্রোগ্রামটি চালালে আমরা নিচের আউটপুট দেখব, যেটি আমরা চাচ্ছিলাম।
1, 2, 3
1, 3, 2
2, 1, 3
2, 3, 1
3, 1, 2
3, 2, 1
যাক, অবশেষে আমাদের সমস্যার সমাধান হলো। তবে আমরা কিন্তু আরও সহজেই সমাধান করতে পারতাম এভাবে–
#include <stdio.h>
int main()
{
int a, b, c;
for (a = 1; a <= 3; a++) {
for (b = 1; b <= 3; b++) {
for (c = 1; c <= 3; c++) {
if(b != a && c != a && c != b) {
printf ("%d, %d, %d\n", a, b, c);
}
}
}
}
return 0;
}
প্রোগ্রাম: ৪.১৪
এখানে আমাদের বেশি চিন্তা করতে হলো না। কেবল প্রিন্ট করার সময় a, b, c
তিনটির মান পরস্পরের সমান নয়, সেটি নিশ্চিত করে নিলেই হলো! বুদ্ধিটা
ভালোই, তবে এটির চেয়ে আমাদের আগের প্রোগ্রামটি কম্পিউটারকে দিয়ে কম কাজ
করায়, তাই চলতেও কম সময় লাগে, বা কম্পিউটারের ভাষায় বললে রান টাইম (run
time) কম। আসলে একটি প্রোগ্রাম চলতে কেমন সময় লাগবে সেটি নির্ভর করে মূলত
প্রোগ্রামটি মোট কয়টি অ্যাসাইনমেন্ট অপারেশন (assignment operation) আর
কয়টি কম্পারিজন অপারেশন (comparison operation) করল তার ওপর।একটুখানি গণিত-
এই অধ্যায়ে আমরা প্রোগ্রামিংয়ের নতুন কিছু শিখব না। এখন পর্যন্ত আমরা
যতটুকু প্রোগ্রামিং শিখেছি, তা দিয়েই কিছু সহজ-সরল গাণিতিক সমস্যার সমাধান
করব।
১) x + y = 15, x – y = 5 হলে x ও y-এর মান কত?
সমীকরণদুটি যোগ করলে পাই 2x = 20, বা x = 10। আবার বিয়োগ করলে পাই, 2y =
10, বা y = 5। এখন একটি প্রোগ্রাম লিখতে হবে যেখানে x + y ও x – y-এর মান
দেওয়া থাকবে, x ও y-এর মান বের করতে হবে। আমি প্রোগ্রামটি একটু পরে লিখে
দেব। এর মধ্যে তুমি নিজে লিখার চেষ্টা করো। সহজ প্রোগ্রাম।
২) 4x + 5y = 14, 5x + 6y = 17 হলে x ও y-এর মান কত?
সমীকরণদুটিকে আমরা এভাবে লিখতে পারি:
a1x + b1y = c1, a2x + b2y = c2। তোমরা বিভিন্নভাবে এর সমাধান করতে পার। এর
মধ্যে দুটি জনপ্রিয় উপায় হচ্ছে প্রতিস্থাপন (substitution) ও
নির্ণায়কের (determinant) সাহায্যে সমাধান। পদ্ধতিগুলো জানা না থাকলে
ক্লাস এইট বা নাইনের গণিত বই দেখো। সমাধান করলে দেখবে,
x = (b2c1 – b1c2) / (a1b2 – a2b1) এবং y = (a1c2 – a2c1) / (a1b2 – a2b1)।
এখন a1, a2, b1, b2, c1, c2-এর জায়গায় নির্দিষ্ট মান বসিয়ে দিলেই x ও
y-এর মান পেয়ে যাবে।
এই ধরনের সমীকরণ সমাধানের জন্যও আমরা একটি প্রোগ্রাম লিখব, যার ইনপুট হবে
a1, a2, b1, b2, c1, c2 এবং আউটপুট হবে x ও y-এর মান। এটিও সহজ প্রোগ্রাম।
নিজে চেষ্টা করো।
আশা করি, তোমরা দুটি সমস্যারই সমাধান নিজে করে ফেলতে পারবে। এখন আমি প্রথম সমস্যার কোড দিচ্ছি:
#include <stdio.h>
int main()
{
double x, y, x_plus_y, x_minus_y;
printf("Enter the value of x + y: ");
scanf("%lf", &x_plus_y);
printf("Enter the value of x - y: ");
scanf("%lf", &x_minus_y);
x = (x_plus_y + x_minus_y) / 2;
y = (x_plus_y - x_minus_y) / 2;
printf("x = %0.2lf, y = %0.2lf\n", x, y);
return 0;
}
প্রোগ্রাম: ৫.১
সমাধান খুবই সহজ। তবে লক্ষ করো যে আমি ভেরিয়েবলের ডাটা টাইপ int ব্যবহার না করে double ব্যবহার করেছি।
এবারে দ্বিতীয় সমস্যার কোড:
#include <stdio.h>
int main()
{
double a1, a2, b1, b2, c1, c2, x, y;
printf("a1 = ");
scanf("%lf", &a1);
printf("a2 = ");
scanf("%lf", &a2);
printf("b1 = ");
scanf("%lf", &b1);
printf("b2 = ");
scanf("%lf", &b2);
printf("c1 = ");
scanf("%lf", &c1);
printf("c2 = ");
scanf("%lf", &c2);
x = (b2 * c1 - b1 * c2) / (a1 * b2 - a2 * b1);
y = (a1 * c2 - a2 * c1) / (a1 * b2 - a2 * b1);
printf("x = %0.2lf, y = %0.2lf\n", x, y);
return 0;
}
প্রোগ্রাম: ৫.২
এটিও সহজ প্রোগ্রাম! তবে তোমরা দেখো (a1 * b2 - a2 * b1)-এর মান আমি দুবার বের করেছি (x-এর মান বের করার সময়, আবার y-এর মান বের করার সময়)। কাজটি একবারেই করা যেত এবং একবারে করলেই ভালো, তাহলে আমাদের প্রোগ্রাম দুটি গুণ ও একটি বিয়োগের কাজ কম করবে। আবার (a1 * b2 - a2 * b1)-এর মান যদি শূন্য হয়, তাহলে একটি ঝামেলা হয়ে যাচ্ছে, কারণ কোনো কিছুকে তো শূন্য দিয়ে ভাগ করা যায় না। তাই ওই মানটি শূন্য হলে আসলে সমীকরণের কোনো সমাধান নেই। এবার প্রোগ্রামটি আরও ভালোভাবে লিখে ফেলি।
#include <stdio.h>
int main()
{
double a1, a2, b1, b2, c1, c2, d, x, y;
printf("a1 = ");
scanf("%lf", &a1);
printf("a2 = ");
scanf("%lf", &a2);
printf("b1 = ");
scanf("%lf", &b1);
printf("b2 = ");
scanf("%lf", &b2);
printf("c1 = ");
scanf("%lf", &c1);
printf("c2 = ");
scanf("%lf", &c2);
d = a1 * b2 - a2 * b1;
if ((int) d == 0) {
printf("Value of x and y can not be determined.\n");
}
else {
x = (b2 * c1 - b1 * c2) / d;
y = (a1 * c2 - a2 * c1) / d;
printf("x = %0.2lf, y = %0.2lf\n", x, y);
}
return 0;
}
প্রোগ্রাম: ৫.৩
এখানে একটি ব্যাপার খেয়াল করো। আমি if-এর ভেতর লিখেছি (int) d == 0। এখানে আমি প্রথমে d (যা একটি double টাইপের ভেরিয়েবল)-কে ইন্টিজারে টাইপ কাস্ট করে তারপর তার মানটি 0-এর সমান কি না তা পরীক্ষা করেছি। পরীক্ষাটা এভাবেও করা যেত: if (d == 0.0) তবে এতে মাঝে মাঝে ঝামেলা হয়, ফ্লোটিং পয়েন্ট-সংক্রান্ত হিসাব-নিকাশের জন্য। তোমরা কম্পিউটার আর্কিটেকচার নিয়ে লেখাপড়া করলে বিষয়টা বুঝতে পারবে।
তোমাদের মনে একটি প্রশ্ন আসতে পারে যে এই সহজ সমস্যাগুলো প্রোগ্রামিং করে সমাধান করে কী লাভ? আসলে একবার প্রোগ্রাম লিখে ফেলার পরে কিন্তু আর সমাধান করতে হয় না। তারপর শুধু ইনপুট দেবে, প্রোগ্রামটি নিজেই সমস্যার সমাধান করে তোমাকে আউটপুট দেবে।
৩) আমি যদি তোমাকে দশ হাজার টাকা ঋণ দিই 35% সুদে এবং টাকাটা পাঁচ বছর সময়ের মধ্যে তোমাকে সুদে-আসলে পরিশোধ করতে বলি, তাহলে পাঁচ বছরে মোট কত টাকা তোমার দিতে হবে এবং প্রতি মাসে কত টাকা দিতে হবে? ঋণটা যদি জটিল কিছু না হয়, তাহলে তোমার মোট পরিশোধ করতে হবে 10000 + 10000 * 35 / 100 টাকা। এই সহজ-সরল ঋণের জন্য একটি প্রোগ্রাম লিখে ফেলা যাক:
#include <stdio.h>
int main()
{
double loan_amount, interest_rate, number_of_years, total_amount, monthly_amount;
printf("Enter the loan amount: ");
scanf("%lf", &loan_amount);
printf("Enter the interest rate: ");
scanf("%lf", &interest_rate);
printf("Number of years: ");
scanf("%lf", &number_of_years);
total_amount = loan_amount + loan_amount * interest_rate / 100.00;
monthly_amount = total_amount / (number_of_years * 12);
printf("Total amount: %0.2lf\n", total_amount);
printf("Monthly amount: %0.2lf\n", monthly_amount);
return 0;
}
প্রোগ্রাম: ৫.৪
আমাদের ফর্মুলাতে একটু সমস্যা আছে। আসলে 35% সুদ দিতে হলে সেটা বাৎসরিক সুদ হবে। অর্থাৎ প্রতি বছর মোট ঋণের উপর 35% সুদ দেওয়া লাগবে। তাহলে দেখা যাচ্ছে পাঁচ বছরে তোমার মোট পরিশোধ করতে হবে 10000 + 10000 * 35 * 5 / 100 টাকা। এখন এই ফর্মুলা অনুযায়ী প্রোগ্রাম লিখে ফেলো।
তবে বাস্তবে ঋণের হিসাব-নিকাশ কিন্তু এত সরল নয়। তুমি ব্যাংক থেকে ঋণ নিতে গেলেই সেটি টের পাবে।
৪) পদার্থবিজ্ঞানের একটি সমস্যার সমাধান করা যাক।
কোনো বস্তু u আদিবেগে (initial velocity) এবং a ত্বরণে (acceleration) যাত্রা শুরু করল (ত্বরণের মান সব সময় a থাকবে, বাড়বে বা কমবে না)। t সময় পরে এর বেগ যদি v হয় তাহলে 2t সময়ে বস্তুটি কত দূরত্ব অতিক্রম করবে? (সমস্যাটি দিয়েছেন শাহরিয়ার মঞ্জুর, এটি ভ্যালাডলিড অনলাইন জাজের 10071 নম্বর সমস্যা)।
2t সময়ে অতিক্রান্ত দূরত্ব হবে v x 2t। এটি প্রমাণ করে ফেলো। তারপর আবার পড়া শুরু করো। নবম-দশম শ্রেণীর পদার্থবিজ্ঞান বইতে তোমরা দুটি সূত্র পাবে:
v = u + at
s = ut + 0.5 at^2 (এখানে s হচ্ছে t সময়ে অতিক্রান্ত দূরত্ব)।
তাহলে 2t সময় পরে অতিক্রান্ত দূরত্ব হবে
u x 2t + 0.5 x a x (2t)^2 = u x 2t + 0.5 x a x 4t^2 = u x 2t + a x 2t^2 = 2t (u + at) = 2tv
এখন, তোমাদেরকে একটি প্রোগ্রাম লিখতে হবে, যেখানে v ও t-এর মান ইনপুট হিসেবে দেওয়া হবে, 2t সময়ে অতিক্রান্ত দূরত্ব নির্ণয় করতে হবে। প্রোগ্রামটি নিজে নিজে লিখে ফেলো।
৫) 1 + 2 + 3 + … + 998 + 999 + 1000 এই ধারার সমষ্টি কত?
তোমরা যারা ধারার যোগফলের সূত্র জানো, তারা চট করে বলে দিতে পারবে, এই ধারাটির যোগফল হচ্ছে 1000 x 1001 / 2। তাহলে এর জন্য একটি প্রোগ্রাম লিখে ফেলা যাক, যেখানে শেষ পদের মান হবে ইনপুট আর আউটপুট হবে যোগফল।
#include <stdio.h>
int main()
{
int n, sum;
scanf("%d", &n);
sum = (n * (n + 1)) / 2;
printf("Summation is %d\n", sum);
return 0;
}
প্রোগ্রাম: ৫.৫
ধারার যোগফল নির্ণয়ের সূত্র জানা না থাকলে আমরা লুপ ব্যবহার করে প্রোগ্রামটি লিখতে পারি।
#include <stdio.h>
int main()
{
int i, n, sum;
scanf("%d", &n);
for(i = 1, sum = 0; i <= n; i++) {
sum = sum + i;
}
printf("Summation is %d\n", sum);
return 0;
}
প্রোগ্রাম: ৫.৬
সুতরাং ধারার সমস্যা নিয়ে আর চিন্তা নেই। তুমি যদি একটি পদের মান তার আগের পদের চেয়ে কত করে বাড়ছে, সেটি বের করতে পারো, তাহলেই লুপ ব্যবহার করে যোগফল বের করে ফেলতে পারবে। তবে সূত্র বের করতে পারলে লুপ ব্যবহার না করাই ভালো। কারণ প্রথম প্রোগ্রামটি দেখো (যেখানে সূত্র ব্যবহার করেছি)। সেখানে একটি যোগ, একটি গুণ আর একটি ভাগ করতে হয়েছে, n-এর মান যত বড়ই হোক না কেন। আর দ্বিতীয় প্রোগ্রামে (যেখানে লুপ ব্যবহার করেছি) n-এর মান যত, ততবার যোগ করতে হয়েছে, আবার সেই যোগফলটি sum ভেরিয়েবলে রাখতে হয়েছে (ভেরিয়েবলে কোনো মান রাখতেও কিন্তু একটু সময় লাগে)।
এখন তোমাদের একটি সহজ প্রোগ্রাম লিখতে হবে। প্রথম n সংখ্যক ধনাত্মক বেজোড় সংখ্যার যোগফল নির্ণয়ের প্রোগ্রাম। n-এর মান হবে ইনপুট, আর যোগফল হবে আউটপুট।
৬) আমাদের এবারকার প্রোগ্রামটি হবে তাপমাত্রাকে সেলসিয়াস (Celsius) থেকে ফারেনহাইটে (Farenheit) রূপান্তর করার প্রোগ্রাম।
সেলসিয়াসকে ফারেনহাইটে রূপান্তরের সূত্র হচ্ছে: °F = (°C × 1.8) + 32।
#include <stdio.h>
int main()
{
double celsius, farenheit;
printf("Enter the temperature in celsius: ");
scanf("%lf", &celsius);
farenheit = 1.8 * celsius + 32;
printf("Temperature in farenheit is: %lf\n", farenheit);
return 0;
}
প্রোগ্রাম: ৫.৭
এখন তোমাদের কাজ হচ্ছে ফারেনহাইট থেকে সেলসিয়াসে রূপান্তরের প্রোগ্রাম লেখা।
৭) এখন আমরা দুটি সংখ্যার গসাগু (GCD → Greatest Common Divisor বা HCF → Highest Common Factor) ও লসাগু (LCM → Least Common Multiple) নির্ণয় করার জন্য প্রোগ্রাম লিখব।
দুটি সংখ্যার গসাগু হচ্ছে যেসব সংখ্যা দিয়ে ওই দুটি সংখ্যা নিঃশেষে বিভাজ্য হয়, তাদের মধ্যে সবচেয়ে বড় সংখ্যা। তাহলে আমরা যেটি করব, দুটি সংখ্যা a ও b নেব। তারপর এদের মধ্যে যেটি ছোট, সেই মানটি আবার x ভেরিয়েবলে রাখব। গসাগু এর মান x-এর চেয়ে বড় হওয়া সম্ভব নয় (5 ও 10-এর গসাগু-এর মান নিশ্চয়ই 5-এর চেয়ে বড় হবে না)। এখন a ও b, x দিয়ে নিঃশেষে বিভাজ্য হয় কি না (a % x == 0 এবং b % x == 0) সেটি পরীক্ষা করব। যদি হয় তবে আমরা গসাগু পেয়ে গেছি। যদি a ও b উভয়েই নিঃশেষে বিভাজ্য না হয়, তখন x-এর মান এক কমিয়ে পরীক্ষা করব। যতক্ষণ না আমরা গসাগু পাচ্ছি x-এর মান কমাতেই থাকব। একসময় আমরা গসাগু পাবই, কারণ x-এর মান যখন 1 হবে, তখন তো x দিয়ে a ও b দুটি সংখ্যাই নিঃশেষে বিভাজ্য। তোমরা কি প্রোগ্রামটি নিজে লিখার চেষ্টা করবে? না পারলে আমার কোড দেখো:
#include <stdio.h>
int main()
{
int a, b, x, gcd;
scanf("%d %d", &a, &b);
if (a < b) {
x = a;
}
else {
x = b;
}
for(; x >= 1; x--) {
if (a % x == 0 && b % x == 0) {
gcd = x;
break;
}
}
printf("GCD is %d\n", gcd);
return 0;
}
প্রোগ্রাম: ৫.৮
প্রোগ্রামে দেখো gcd পাওয়ার সঙ্গে সঙ্গে লুপ থেকে বের হয়ে যেতে হবে (আমি break ব্যবহার করেছি এই জন্য)। break ব্যবহার না করলে কী হবে সেটি পরীক্ষা করে দেখো।
তবে গসাগু বের করার জন্য আমি যেই পদ্ধতি ব্যবহার করেছি সেটি খুব সহজ পদ্ধতি হলেও ইফিশিয়েন্ট (efficient) নয়। যেমন, সংখ্যা দুটি খুব বড় হলে এবং সহমৌলিক (co-prime) হলে লুপটি কিন্তু অনেকবার ঘুরবে। কারণ সহমৌলিক হলে গসাগু হবে 1। তোমরা নিশ্চয়ই জানো যে, দুটি সংখ্যার মধ্যে 1 ছাড়া আর কোনো সাধারণ উৎপাদক না থাকলে সংখ্যা দুটি সহমৌলিক।
গসাগু বের করার জন্য ইউক্লিডের একটি চমৎকার পদ্ধতি আছে। ইউক্লিড ভাগশেষ উপপাদ্যের (division algorithm) সাহায্যে গসাগু বের করার উপায় দেখিয়েছেন। এই পদ্ধতিতে খুব সহজে গসাগু বের করা যায় এবং প্রোগ্রামটিও বেশ ইফিশিয়েন্ট হয়। এর জন্য দুটি জিনিস জানা লাগবে:
a ও 0-এর গসাগু-এর মান a।
a ও b-এর গসাগু = b ও a % b-এর গসাগু।
তাহলে প্রোগ্রামে যেটি করতে হবে, একটি লুপের সাহায্যে a-এর মান b আর b-এর মান a%b বসিয়ে যেতে হবে, যতক্ষণ না b-এর মান শূন্য হয়। b-এর মান শূন্য হলেই বুঝে যাব যে গসাগু হচ্ছে a (এটা কিন্তু প্রোগ্রাম শুরুর সময় a-এর মান না, b-এর মান যখন শূন্য হবে সেই সময় a-এর মান)।
#include <stdio.h>
int main()
{
int a, b, t, x, gcd;
scanf("%d %d", &a, &b);
if (a == 0) gcd = b;
else if (b == 0) gcd = a;
else {
while (b != 0) {
t = b;
b = a % b;
a = t;
}
gcd = a;
}
printf("GCD is %d\n", gcd);
return 0;
}
প্রোগ্রাম: ৫.৯
এই প্রোগ্রামটি আরও ইফিশিয়েন্ট করার চেষ্টা করো।
এবার লসাগু বের করার প্রোগ্রাম। তোমরা নিশ্চয়ই স্কুলে শিখেছ, কীভাবে লসাগু বের করতে হয়। সেই পদ্ধতি অবলম্বন করে প্রোগ্রাম লিখে ফেলো। আর যারা সেই পদ্ধতি জানো না, তাদের জন্য একটি সূত্র বলে দিচ্ছি। আশা করি, লসাগু বের করার প্রোগ্রাম লিখতে আর সমস্যা হবে না।
দুটি সংখ্যার লসাগু x দুটি সংখ্যার গসাগু = সংখ্যা দুটির গুণফল।
অ্যারে---
এতক্ষণে তোমাদের প্রোগ্রামিং জ্ঞান-বুদ্ধি একটু বেড়েছে। চলো, এবার তাহলে
কিছু জনসেবামূলক কর্মকাণ্ড করা যাক। আমরা স্কুলের প্রিয় গণিত শিক্ষকের
জন্য পরীক্ষার ফলাফল বের করার প্রোগ্রাম লিখে দেব। ওই স্কুলে প্রথম
সাময়িক, দ্বিতীয় সাময়িক ও বার্ষিক এই তিনটি পরীক্ষাই 100 নম্বরের হয়।
তারপর বার্ষিক পরীক্ষার 50%, দ্বিতীয় সাময়িক পরীক্ষার 25% ও প্রথম
সাময়িক পরীক্ষার 25% নিয়ে চূড়ান্ত ফলাফল প্রকাশ করা হয়। তাহলে আমাদের
প্রোগ্রামের ইনপুট হচ্ছে ওই তিনটি পরীক্ষার নম্বর। আমাদেরকে চূড়ান্ত ফলাফল
দেখাতে হবে। এটি কোনো ব্যাপারই নয়:
#include <stdio.h>
int main()
{
int ft_marks, st_marks, final_marks;
double total_marks;
ft_marks = 80;
st_marks = 74;
final_marks = 97;
total_marks = ft_marks / 4.0 + st_marks / 4.0 + final_marks / 2.0;
printf("%0.0lf\n", total_marks);
return 0;
}
প্রোগ্রাম: ৬.১
প্রোগ্রামটির আউটপুট 87। (কিন্তু আমি যদি total_marks = ft_marks / 4.0 + st_marks / 4.0 + final_marks / 2.0; না লিখে এভাবে লিখতাম total_marks = ft_marks / 4 + st_marks / 4 + final_marks / 2; তাহলে আউটপুট আসে 86। কারণ কী? কম্পিউটারের মাথা খারাপ নাকি আমার?)
আমরা কিন্তু আমাদের প্রিয় শিক্ষকের তেমন কোনো উপকার করতে পারলাম না। কারণ তাঁর ক্লাসে মোট ছাত্রছাত্রীর সংখ্যা চল্লিশ। তাহলে স্যারকে চল্লিশবার প্রোগ্রামটি চালাতে হবে! কিন্তু এটি তো কোনো কাজের কথা হলো না। আমাদের উচিত, সবার চূড়ান্ত ফলাফল একটি প্রোগ্রামের মাধ্যমে নির্ণয় করা। তেমন কোনো কঠিন কাজ নয় এটি। আমরা এমন একটি প্রোগ্রাম লেখা শুরু করে দিতে পারি:
#include
int main()
{
int ft_marks_1, st_marks_1, final_marks_1, ft_marks_2, st_marks_2, final_marks_2, ft_marks_3, st_marks_3, final_marks_3,
তোমরা নিশ্চয়ই বুঝতে পারছ, আমি কী করতে যাচ্ছি? বলো তো এভাবে প্রোগ্রামটি লিখতে গেলে মোট কয়টি ভেরিয়েবলের দরকার? 160টি। স্যারের কষ্ট কমাতে গিয়ে আমাদের কষ্ট এত বাড়ানোর কোনো মানে হয় না। কিন্তু এধরনের প্রোগ্রাম তো আমাদের প্রায়ই লিখতে হবে। চিন্তা নেই! প্রায় সব প্রোগ্রামিং ল্যাংগুয়েজেই অ্যারে (Array) নামে একটি চমৎকার জিনিস আছে। এতে একই ধরনের অনেকগুলো ভেরিয়েবল একসঙ্গে রাখা যায়। ভেরিয়েবলের যেমন নাম রাখি, অ্যারের বেলাতেও তেমন একটি নাম দিতে হয়। Cতেও অ্যারে আছে।
ভেরিয়েবলের যেমন একটি ডাটা টাইপ থাকে, অ্যারেরও থাকে। অ্যারেটি যে ডাটা টাইপের হবে তাতে কেবল সেই রকম ডাটাই রাখা যাবে। যেমন char টাইপের অ্যারেতে কেবল char টাইপের জিনিস থাকবে।
অ্যারেতে কয়টি উপাদান থাকবে সেটি শুরুতেই বলে দিতে হয়।
int ara[10]; এভাবে আমরা একটি অ্যারে ডিক্লেয়ার করতে পারি, যার নাম হচ্ছে ara, যেটিতে কেবল ইন্টিজার টাইপের ডাটা থাকবে আর এই অ্যারেতে মোট দশটি সংখ্যা রাখা যাবে। প্রথমটি হচ্ছে ara[0] (হ্যাঁ, ara[1] না কিন্তু), দ্বিতীয়টি ara[1], তৃতীয়টি ara[2], এভাবে দশম সংখ্যাটি হচ্ছে ara[9]। অর্থাৎ, ara[i] হচ্ছে i+1তম উপাদান।
এবারে চলো অ্যারে নিয়ে একটু খেলাধুলা করা যাক। প্রতিটি প্রোগ্রাম কিন্তু অবশ্যই কম্পিউটারে চালিয়ে দেখবে।
#include <stdio.h>
int main()
{
int ara[5] = {10, 20, 30, 40, 50};
printf("First element: %d\n", ara[0]);
printf("Third element: %d\n", ara[2]);
return 0;
}
প্রোগ্রাম: ৬.২
আউটপুট ঠিকঠাক দেখতে পাচ্ছ?
আরেকটি প্রোগ্রাম:
#include <stdio.h>
int main()
{
int ara[5] = {6, 7, 4, 6, 9};
printf("%d\n", ara[-1]);
printf("%d\n", ara[5]);
printf("%d\n", ara[100]);
return 0;
}
প্রোগ্রাম: ৬.৩
এটির জন্য কী আউটপুট আসা উচিত? আমি জানি না এবং এটি জানা সম্ভব নয়। যেকোনো ধরনের সংখ্যা আসতে পারে। এগুলোকে গারবেজ (garbage) বলে। কারণ আসলে তো ওই অ্যারেতে -1, 5, 100 এই ইনডেক্স বলতে কিছু নেই। অ্যারেটির দৈর্ঘ্যই হচ্ছে 5 সুতরাং ইনডেক্স হবে 0 থেকে 4।
এখন কোনো অ্যারের সব উপাদান যদি একসঙ্গে দেখাতে চাই, তাহলে উপায় কী? উপায় হচ্ছে প্রথম উপাদান (ara[0]), দ্বিতীয় উপাদান (ara[1]), তৃতীয় উপাদান (ara[2]) … এভাবে একে একে সবগুলো প্রিন্ট করা। আর তার জন্য অবশ্যই আমরা লুপের সাহায্য নেব।
#include <stdio.h>
int main()
{
int ara[10] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
int i;
for(i = 0; i < 10; i++) {
printf("%d th element is: %d\n", i+1, ara[i]);
}
return 0;
}
প্রোগ্রাম: ৬.৪
আর যদি শেষ উপাদান থেকে প্রথম উপাদান পর্যন্ত দেখাতে হতো? কোনো সমস্যা নেই, শুধু লুপে এ indexটি 9 থেকে 0 পর্যন্ত আনলেই চলবে। এখন তোমরা প্রোগ্রামটি লিখে ফেলো।
এবারে একটি ছোট সমস্যা। কোনো একটি অ্যারেতে দশটি উপাদান আছে, সেগুলো বিপরীত ক্রমে রাখতে হবে। অর্থাৎ দশম উপাদানটি হবে প্রথম উপাদান, প্রথমটি হবে দশম, দ্বিতীয়টি হবে নবম, নবমটি হবে দ্বিতীয়.. এই রকম। তার জন্য আমরা যেটি করতে পারি, আরেকটি অ্যারের সাহায্য নিতে পারি। দ্বিতীয় অ্যারেটিতে প্রথম অ্যারের উপাদানগুলো বিপরীত ক্রমে রাখবো। তারপর দ্বিতীয় অ্যারেটি প্রথম অ্যারেতে কপি করে ফেলব।
#include <stdio.h>
int main()
{
int ara[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
int ara2[10];
int i, j;
for(i = 0, j = 9; i < 10; i++, j--) {
ara2[j] = ara[i];
}
for(i = 0; i < 10; i++) {
ara[i] = ara2[i];
}
for(i = 0; i < 10; i++) {
printf("%d\n", ara[i]);
}
return 0;
}
প্রোগ্রাম: ৬.৫
এখানে লক্ষ করো যে প্রথম অ্যারেটির ক্ষেত্রে আমি তৃতীয় বন্ধনীর ভেতর অ্যারের উপাদান সংখ্যা বলে দিইনি, কারণ সি-এর কম্পাইলার দ্বিতীয় বন্ধনীর ভেতর সংখ্যাগুলো দেখেই বুঝে নিতে পারে যে araতে দশটি উপাদান আছে। দ্বিতীয় অ্যারে অর্থাৎ ara2তে এখন কোনো কিছু নেই। তাই শুরুতেই বলে দিতে হবে যে তাতে কয়টি উপাদান থাকবে। তাহলে কম্পাইলার সেই অনুসারে কম্পিউটারের মেমোরির মধ্যে অ্যারের জন্য জায়গা করে নেবে।
প্রোগ্রামটি ভালোভাবেই কাজ করছে। কিন্তু তোমরা একটু চিন্তাভাবনা করলেই বুঝতে পারবে যে দ্বিতীয় অ্যারেটি ব্যবহার করার কোনো দরকার ছিল না। আমরা একটি বহুল প্রচলিত পদ্ধতিতেই কাজটি করতে পারতাম।
int temp;
temp = ara[9];
ara[9] = ara[0];
ara[0] = temp;
প্রথম ও দশম উপাদান অদলবদল হয়ে গেল। তারপর
temp = ara[8];
ara[8] = ara[1];
ara[1] = temp;
দ্বিতীয় ও নবম উপাদান অদলবদল হয়ে গেল। তাহলে চলো প্রোগ্রামটি লিখে ফেলি:
#include <stdio.h>
int main()
{
int ara[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
int i, j, temp;
for(i = 0, j = 9; i < 10; i++, j--) {
temp = ara[j];
ara[j] = ara[i];
ara[i] = temp;
}
for(i = 0; i < 10; i++) {
printf("%d\n", ara[i]);
}
return 0;
}
প্রোগ্রাম: ৬.৬
প্রোগ্রামটি চালাও। কী দেখলে? আউটপুট কি এরকম?
10
20
30
40
50
60
70
80
90
100
তারমানে কাজ হয়নি! আসলে আমি একটি ছোট্ট ভুল করেছি, সেটি তোমরা খুঁজে বের করো। এ ধরনের ভুলকে বলে বাগ (bug), তখন প্রোগ্রাম ঠিকমতো রান করে কিন্তু সঠিক আউটপুট দেয় না। আমার কোডে বাগ আছে, তোমরা ডিবাগ (debug) করো (মানে বাগটি বের করে ঠিক করো)।
এখন চলো আমাদের আগের সমস্যায় ফিরে যাই। আমরা এখন প্রথম সাময়িক পরীক্ষায় সবার গণিতের নম্বর একটি অ্যারেতে রাখব, দ্বিতীয় সাময়িক পরীক্ষার নম্বর আরেকটি অ্যারেতে, বার্ষিক পরীক্ষার নম্বরের জন্য আরও একটি এবং রেজাল্টের জন্যও একটি অ্যারে ব্যবহার করব।
int ft_marks[40], st_marks[40], final_marks[40];
double total_marks[40];
যার রোল নম্বর 1 তার নম্বরগুলো থাকবে অ্যারের প্রথম ঘরে (মানে index 0 হবে)। এখন বলো তো total_marks[34]-এ কার সর্বমোট নম্বর আছে? যার রোল নম্বর 35। তাহলে কারও রোল নম্বর n হলে তার সর্বমোট নম্বর হচ্ছে total_marks[n-1]।
এখন প্রোগ্রামটি লিখে ফেলা যাক:
#include <stdio.h>
int main()
{
int ft_marks[40] = {83, 86, 97, 95, 93, 95, 86, 52, 49, 41, 42, 47, 90, 59, 63, 86, 40, 46, 92, 56, 51, 48, 67, 49, 42, 90, 42, 83, 47, 95, 69, 82, 82, 58, 69, 67, 53, 56, 71, 62},
st_marks[40] = {86, 97, 95, 93, 95, 86, 52, 49, 41, 42, 47, 90, 59, 63, 86, 40, 46, 92, 56, 51, 48, 67, 49, 42, 90, 42, 83, 47, 95, 69, 82, 82, 58, 69, 67, 53, 56, 71, 62, 49},
final_marks[40] = {87, 64, 91, 43, 89, 66, 58, 73, 99, 81, 100, 64, 55, 69, 85, 81, 80, 67, 88, 71, 62, 78, 58, 66, 98, 75, 86, 90, 80, 85, 100, 64, 55, 69, 85, 81, 80, 67, 88, 71};
int i;
double total_marks[40];
for(i = 0; i < 40; i++) {
total_marks[i] = ft_marks[i] / 4.0 + st_marks[i] / 4.0 + final_marks[i] / 2.0;
}
for(i = 1; i <= 40; i++) {
printf("Roll NO: %d\tTotal Marks: %0.0lf\n", i, total_marks[i-1]);
}
return 0;
}
প্রোগ্রাম: ৬.৭
রান করে দেখো, কী সুন্দর আউটপুট! printf ফাংশনের ভেতরে দেখো এক জায়গায় আমি \t লিখেছি, এতে ট্যাব (Tab) প্রিন্ট হবে (কিবোর্ডের বাঁ দিকে দেখো)। রোল নং প্রিন্ট করার পরে একটি ট্যাব দিয়ে টোটাল মার্কস প্রিন্ট করলে দেখতে একটু ভালো লাগে এই জন্য \t ব্যবহার করেছি, এমনিতে কোনো দরকার নেই।
কিন্তু এত সুন্দর প্রোগ্রাম দেখে তোমার শিক্ষক কোথায় তোমাকে একটু চটপটি খাওয়াবেন না উল্টা আরেকটি আবদার করে বসলেন। কোন নম্বর কতজন পেয়েছে সেটি উনি দেখতে চান। মানে 50 কতজন পেল, 51 কতজন পেল … এই রকম আর কি। বাকি অংশ পড়ার আগে প্রোগ্রামটি তোমরা নিজে নিজে লিখার চেষ্টা করো। এখন ইচ্ছা না করলে বইটি পড়া বন্ধ করে দাও এবং পরে কোনো একসময় চেষ্টা করবে।
আশা করি, তোমাদের মধ্যে কেউ কেউ প্রোগ্রামটি লিখে ফেলেছ। যদি কমপক্ষে এক ঘণ্টা চেষ্টার পরেও লিখতে না পারো তাহলে এখন আমরা সমাধানের চেষ্টা করতে পারি। শুরুতেই একটি ব্যাপার খেয়াল করো যে কেউ কিন্তু 50-এর নিচে নম্বর পায়নি। তাই 50 থেকে 100 পর্যন্ত কোন নম্বর কতজন পেল সেটি বের করলেই চলবে। আমার মাথায় প্রথমেই যে সমাধান আসছে সেটি হলো total_marks অ্যারেতে প্রথমে দেখব, কয়টি 50 আছে, তারপর আবার দেখব কয়টি 51 আছে … এভাবে 100 পর্যন্ত দেখব। মানে 50 থেকে 100 পর্যন্ত সব সংখ্যার জন্য total_marks অ্যারেতে সংখ্যাগুলো চেক করব।
for(marks = 50; marks <= 100; marks++) { লুপের সাহায্যে প্রথমে marks-এর মান 50, তারপরে 51, এভাবে এক এক করে বাড়াব 100 পর্যন্ত।
count = 0; ধরে নিচ্ছি শূন্য জন 'marks' নম্বর পেয়েছে। marks-এর সব কটি মানের জন্যই প্রথমে আমরা এই কাজটি করব। এবারে total_marks অ্যারেতে দেখব যে কোনো নম্বর যদি marks-এর সমান হয়, তবে count-এর মান এক বাড়িয়ে দেব। তাহলে কোনো একটি নম্বর (marks) যতবার অ্যারেতে আছে, count-এর মান তত হবে।
for(i = 0; i < 40; i++) {
if(total_marks[i] == marks) {
count++;
}
}
printf("Marks: %d Count: %d\n", marks, count); এখানে আমরা প্রতিটি marks এবং সেটি কতবার আছে (count) তা প্রিন্ট করে দিচ্ছি।
}
তাহলে পুরো প্রোগ্রাম লিখে ফেলি:
#include <stdio.h>
int main()
{
int marks, i, count;
int total_marks[] = {86, 78, 94, 68, 92, 78, 64, 62, 72, 61, 72, 66, 65, 65, 80, 72, 62, 68, 81, 62, 56, 68, 58, 56, 82, 70, 74, 78, 76, 84, 88, 73, 62, 66, 76, 70, 67, 65, 77, 63};
for(marks = 50; marks <= 100; marks++) {
count = 0;
for(i = 0; i < 40; i++) {
if(total_marks[i] == marks) {
count++;
}
}
printf("Marks: %d Count: %d\n", marks, count);
}
return 0;
}
প্রোগ্রাম: ৬.৮
তেমন কঠিন কিছু নয়। নেস্টেড ফর লুপ ব্যবহার করে সহজ-সরল সমাধান করে ফেললাম। আচ্ছা বলো তো if-এর ভেতর যে শর্তটি আমরা পরীক্ষা করছি (total_marks[i] == marks) এই কাজটি প্রোগ্রামে কতবার হয়? বাইরের লুপটি ঘুরবে 51 বার এবং প্রতিবারের জন্য ভেতরের লুপটি ঘুরবে 40 বার। তাহলে মোট 51 x 40 = 2040 বার।
ওপরের প্রোগ্রামটি আমরা এখন একটু অন্যভাবে লিখার চেষ্টা করব। নিচের প্রোগ্রামটি চটপট টাইপ করে ফেলো এবং রান করো:
#include <stdio.h>
int main()
{
int i;
int total_marks[] = {86, 78, 94, 68, 92, 78, 64, 62, 72, 61, 72, 66, 65, 65, 80, 72, 62, 68, 81, 62, 56, 68, 58, 56, 82, 70, 74, 78, 76, 84, 88, 73, 62, 66, 76, 70, 67, 65, 77, 63};
int marks_count[101];
for(i = 0; i < 101; i++) {
marks_count[i] = 0;
}
for(i = 0; i < 40; i++) {
marks_count[total_marks[i]]++;
}
for(i = 50; i <= 100; i++) {
printf("Marks: %d Count: %d\n", i, marks_count[i]);
}
return 0;
}
প্রোগ্রাম: ৬.৯
এখানে আমি যেটি করেছি, একটি অতিরিক্ত অ্যারে ব্যবহার করেছি। marks_count একটি ইন্টিজার টাইপের অ্যারে এবং marks_count[n] দিয়ে আমরা বুঝব n সংখ্যাটি কতবার total_marks-এর মধ্যে আছে। নম্বর যেহেতু 0 থেকে 100-এর মধ্যে হতে পারে তাই আমরা ওই অ্যারেতে মোট 101টি সংখ্যা রাখার ব্যবস্থা করলাম। int marks_count[101];
শুরুতে যেহেতু কিছুই জানি না, তাই ধরে নিই, সব সংখ্যা শূন্য বার আছে। তাই marks_count অ্যারের সব ঘরে 0 বসিয়ে দিই:
for(i = 0; i < 101; i++) { marks_count[i] = 0; } এখন total_marks অ্যারের প্রতিটি সংখ্যার জন্য marks_count অ্যারের ওই ঘরের মান এক বাড়িয়ে দিই। for(i = 0; i < 40; i++) { marks_count[total_marks[i]]++; } বুঝতে সমস্যা হচ্ছে নাকি? একটু চিন্তা করো। যখন i-এর মান 0, তখন total_marks[i] হচ্ছে total_marks[0], অর্থাৎ 86। এখন আমাদের দরকার হচ্ছে marks_count অ্যারের ওই ঘরটার (মানে marks_count[86]) মান এক বাড়িয়ে দেওয়া। শুরুতে ছিল শূন্য, এখন হবে এক। আমরা কিন্তু সে কাজটিই করেছি marks_count[total_marks[i]]-এর মান এক বাড়িয়ে দিয়েছি marks_count[total_marks[i]]++; আসলে ব্যাপারটি এইভাবেও লেখা যেত: t_m = total_marks[i]; marks_count[t_m]++; এখনো যারা মাথা চুলকাচ্ছ তারা নিচের প্রোগ্রামটি কম্পিউটারে রান করাও। এখানে প্রতিবার marks_count[total_marks[i]]++; করার পরে marks_count অ্যারেটি আমরা এক লাইনে প্রিন্ট করেছি।
#include <stdio.h>
int main()
{
int i, j;
int total_marks[] = {6, 7, 4, 6, 9, 7, 6, 2, 4, 3, 4, 1};
int marks_count[11];
for(i = 0; i < 11; i++) {
marks_count[i] = 0;
}
for(i = 0; i < 12; i++) {
marks_count[total_marks[i]]++;
for(j = 0; j <= 10; j++) {
printf("%d ", marks_count[j]);
}
printf("\n");
}
return 0;
}
প্রোগ্রাম: ৬.১০
ফাংশন (Function)-----
তোমরা
কি একটি মজার ব্যাপার জানো? একজন লেখক সারা জীবনে যতটা সময় লেখেন তার চেয়ে
বেশি সময় তিনি অন্যের লেখা পড়েন? ব্যাপারটি প্রোগ্রামারদের বেলাতেও সত্য।
একজন প্রোগ্রামার তার প্রোগ্রামিং জীবনে যতটা সময় নিজে কোড লেখে তার চেয়ে
বেশি সময় অন্যের লেখা কোড পড়ে! তাই কোড লিখার সময় খেয়াল রাখতে হবে, যেন
সেটি পড়াও সুবিধাজনক হয়।
যারা বইটি শুরু থেকে পড়ে এসেছ
তারা ইতিমধ্যে অনেকবার ফাংশন শব্দটি দেখেছ। যারা আরও বেশি মনোযোগ দিয়ে পড়েছ
তারা এটিও খেয়াল করেছ যে printf, scanf ইত্যাদি, যেগুলো তোমরা ব্যবহার করছ
সেগুলো একেকটি ফাংশন। আবার mainও একটি ফাংশন। আমরা এবার দেখব ফাংশন
ব্যাপারটি আসলে কী, এর দরকারটাই বা কী। আর তারপর আমরা নিজেদের ফাংশন তৈরি
করা শিখব।
ফাংশন ব্যবহার করা হয় কোনো একটি
নির্দিষ্ট কাজ করার জন্য। যেমন printf ফাংশনটি দিয়ে আমরা মনিটরে আউটপুট
দিই। আবার scanf, getchar এসব ফাংশন দিয়ে আমরা কিবোর্ড থেকে ইনপুট নিই। এখন
printf ফাংশনটি যে আমরা লিখলাম, কম্পিউটারের তো আর এটি বোঝার কথা নয়।
printf ফাংশনটি কী কাজ করবে, কীভাবে করবে সেটি আসলে বলে দেওয়া আছে stdio.h
নামের একটি হেডার (header) ফাইলের মধ্যে। এজন্যই আমরা আমাদের
প্রোগ্রামগুলোতে (যেখানে printf, scanf ইত্যাদি ব্যবহার করেছি) ওই হেডার
ফাইলটির কথা বলে দিই (#include )। আবার স্ট্রিং-সংক্রান্ত
ফাংশনগুলো ব্যবহার করলে string.h – এই হেডার ফাইলটির কথাও বলে দিই। এখন
চিন্তা করো, printf ফাংশনের এই কোডটি যদি আমাদের নিজেদের লিখতে হতো, তাহলে
ব্যাপারটি কী বিরক্তিকরই না হতো! এরকম অনেক ফাংশন আছে যেগুলোর ব্যবহার
তোমরা আস্তে আস্তে জেনে যাবে।
আচ্ছা, main কে ও তো আমি একটি ফাংশন বলেছি, কিন্তু এটি দিয়ে আমরা আবার কী করি? সি ল্যাঙ্গুয়েজে এই ফাংশনটি দিয়েই আসলে আমরা একটি প্রোগ্রাম চালাই। কম্পাইলার জানে যে main ফাংশন যেখানে আছে, সেখান থেকেই কাজ শুরু করতে হবে। তাই একটি প্রোগ্রামে কেবল একটিই main ফাংশন থাকে।
এবারে দেখি, আমরা নিজেরা কীভাবে ফাংশন তৈরি করতে পারি। একটি ফাংশন যখন আমরা তৈরি করব সেটির গঠন হবে মোটামুটি এই রকম:
return_type function_name (parameters) {
function_body
return value
}
return_type: এখানে বলে দিতে হবে ফাংশনটি কাজ শেষ করে বের হবার সময় কী ধরনের ডাটা রিটার্ন করবে। সেটি, int, double এসব হতে পারে। আবার কিছু রিটার্ন করতে না চাইলে সেটি void হতে পারে। অর্থাৎ সে কিছুই রিটার্ন করবে না। এর মানে দাঁড়াচ্ছে, তুমি আসলে ফাংশনকে দিয়ে কোনো একটি কাজ করাবে, সেজন্য কাজ শেষে সে তোমাকে কী ধরনের ডাটা ফেরত দেবে সেটি বলে দিতে হবে। ফাংশনের কোনো জায়গাতে তুমি যখনই return ব্যবহার করবে, ফাংশনটি সেই জায়গা থেকেই রিটার্ন করবে বা বের হয়ে যাবে। অনেক ফাংশনের ভেতর দেখবে একাধিক রিটার্ন আছে এবং সঙ্গে বিভিন্ন শর্ত দেওয়া আছে। শর্তের উপর নির্ভর করে যখনই প্রোগ্রামটি কোনো রিটার্ন পাবে তখনই ফাংশন থেকে বের হয়ে যাবে।
function_name: এখানে আমাদের ফাংশনের নাম লিখতে হবে। ফাংশনের নাম হতে হবে অর্থপূর্ণ যাতে নাম দেখেই ধারনা করা যায় যে ফাংশনটি কী কাজ করবে। যেমন কোন সংখ্যার বর্গমূল নির্ণয়ের জন্য যদি আমরা একটি ফাংশন লিখি তবে সেটির নাম আমরা দিতে পারি square_root বা sqrt। আমরা নিশ্চয়ই সেটির নাম beautiful দিব না, যদিও কম্পাইলার তাতে কোন আপত্তি করবে না।
parameters: এখানে ফাংশনটি কাজ করার জন্য প্রয়োজনীয় ডাটা আমরা দেব। যেমন স্ট্রিং-এর দৈর্ঘ্য নির্ণয়ের জন্য আমরা যখন strlen ফাংশনটি ব্যবহার করি সেখানে কোন স্ট্রিং-এর দৈর্ঘ্য নির্ণয় করতে হবে সেটি বলে দিতে হয় (নইলে সেটি কার দৈর্ঘ্য নির্ণয় করবে?)। আবার বর্গমূল নির্ণয়ের জন্য ফাংশন লিখলে কোন সংখ্যার বর্গমূল বের করতে হবে সেটি বলে দিতে হবে। প্যারামিটারের মাধ্যমে আমরা সেসব ডাটা ওই ফাংশনের কাছ পাঠাতে পারি। আবার কোনো কিছু পাঠাতে না চাইলে সেটি খালিও রাখতে পারি। যেমন, getchar() বা main() ফাংশন। একাধিক প্যারামিটার পাঠানোর সময় প্রতিটি প্যারামিটার কমা (,) দিয়ে আলাদা করতে হবে।
function_body: ফাংশনটি কীভাবে কী কাজ করবে সেটি বডিতে বলে দিতে হবে। মানে কোড লিখতে হবে আর কি।
return value: ফাংশনটি কাজ শেষ করে, তাকে যে জায়গা থেকে কল করা হয়েছে সে জায়গায় ফিরে যায়। ফেরার সময় আমরা কোনো মান পাঠাতে পারি। যেমন sqrt() ফাংশনে আমরা চাই সে বর্গমূল বের করবে। তো বর্গমূলটি বের করে তো সেটি ফেরত পাঠাবার ব্যবস্থা রাখতে হবে? বর্গমূলটির মান যদি x হয়, তবে আমরা return x; স্টেটমেন্ট দিয়ে সেটির মান ফেরত পাঠাব।
int root = sqrt(25);
এখানে sqrt ফাংশন 25-এর বর্গমূল নির্ণয় করার পর বর্গমূলটি ফেরত পাঠাবে এবং সেটি root নামের একটি ইন্টিজার ভেরিয়েবলে জমা হবে।
একটি উদাহরণ দিই। তোমরা যারা ত্রিকোণমিতি পড়েছ তারা নিশ্চয়ই sin, cos, tan ইত্যাদির সঙ্গে পরিচিত। sin 300-এর মান হচ্ছে 0.5। এখানে sin কিন্তু আসলে একটি ফাংশন, যার প্যারামিটার হিসেবে আমরা কোণের মান দিচ্ছি। আর ফাংশনটি ওই কোণের sine (সংক্ষেপে sin)-এর মান রিটার্ন করছে।
এবারে চলো, আর বকবক না করে প্রোগ্রামিং শুরু করে দিই। তারপর দেখি কী করলে কী হয়।
আচ্ছা, main কে ও তো আমি একটি ফাংশন বলেছি, কিন্তু এটি দিয়ে আমরা আবার কী করি? সি ল্যাঙ্গুয়েজে এই ফাংশনটি দিয়েই আসলে আমরা একটি প্রোগ্রাম চালাই। কম্পাইলার জানে যে main ফাংশন যেখানে আছে, সেখান থেকেই কাজ শুরু করতে হবে। তাই একটি প্রোগ্রামে কেবল একটিই main ফাংশন থাকে।
এবারে দেখি, আমরা নিজেরা কীভাবে ফাংশন তৈরি করতে পারি। একটি ফাংশন যখন আমরা তৈরি করব সেটির গঠন হবে মোটামুটি এই রকম:
return_type function_name (parameters) {
function_body
return value
}
return_type: এখানে বলে দিতে হবে ফাংশনটি কাজ শেষ করে বের হবার সময় কী ধরনের ডাটা রিটার্ন করবে। সেটি, int, double এসব হতে পারে। আবার কিছু রিটার্ন করতে না চাইলে সেটি void হতে পারে। অর্থাৎ সে কিছুই রিটার্ন করবে না। এর মানে দাঁড়াচ্ছে, তুমি আসলে ফাংশনকে দিয়ে কোনো একটি কাজ করাবে, সেজন্য কাজ শেষে সে তোমাকে কী ধরনের ডাটা ফেরত দেবে সেটি বলে দিতে হবে। ফাংশনের কোনো জায়গাতে তুমি যখনই return ব্যবহার করবে, ফাংশনটি সেই জায়গা থেকেই রিটার্ন করবে বা বের হয়ে যাবে। অনেক ফাংশনের ভেতর দেখবে একাধিক রিটার্ন আছে এবং সঙ্গে বিভিন্ন শর্ত দেওয়া আছে। শর্তের উপর নির্ভর করে যখনই প্রোগ্রামটি কোনো রিটার্ন পাবে তখনই ফাংশন থেকে বের হয়ে যাবে।
function_name: এখানে আমাদের ফাংশনের নাম লিখতে হবে। ফাংশনের নাম হতে হবে অর্থপূর্ণ যাতে নাম দেখেই ধারনা করা যায় যে ফাংশনটি কী কাজ করবে। যেমন কোন সংখ্যার বর্গমূল নির্ণয়ের জন্য যদি আমরা একটি ফাংশন লিখি তবে সেটির নাম আমরা দিতে পারি square_root বা sqrt। আমরা নিশ্চয়ই সেটির নাম beautiful দিব না, যদিও কম্পাইলার তাতে কোন আপত্তি করবে না।
parameters: এখানে ফাংশনটি কাজ করার জন্য প্রয়োজনীয় ডাটা আমরা দেব। যেমন স্ট্রিং-এর দৈর্ঘ্য নির্ণয়ের জন্য আমরা যখন strlen ফাংশনটি ব্যবহার করি সেখানে কোন স্ট্রিং-এর দৈর্ঘ্য নির্ণয় করতে হবে সেটি বলে দিতে হয় (নইলে সেটি কার দৈর্ঘ্য নির্ণয় করবে?)। আবার বর্গমূল নির্ণয়ের জন্য ফাংশন লিখলে কোন সংখ্যার বর্গমূল বের করতে হবে সেটি বলে দিতে হবে। প্যারামিটারের মাধ্যমে আমরা সেসব ডাটা ওই ফাংশনের কাছ পাঠাতে পারি। আবার কোনো কিছু পাঠাতে না চাইলে সেটি খালিও রাখতে পারি। যেমন, getchar() বা main() ফাংশন। একাধিক প্যারামিটার পাঠানোর সময় প্রতিটি প্যারামিটার কমা (,) দিয়ে আলাদা করতে হবে।
function_body: ফাংশনটি কীভাবে কী কাজ করবে সেটি বডিতে বলে দিতে হবে। মানে কোড লিখতে হবে আর কি।
return value: ফাংশনটি কাজ শেষ করে, তাকে যে জায়গা থেকে কল করা হয়েছে সে জায়গায় ফিরে যায়। ফেরার সময় আমরা কোনো মান পাঠাতে পারি। যেমন sqrt() ফাংশনে আমরা চাই সে বর্গমূল বের করবে। তো বর্গমূলটি বের করে তো সেটি ফেরত পাঠাবার ব্যবস্থা রাখতে হবে? বর্গমূলটির মান যদি x হয়, তবে আমরা return x; স্টেটমেন্ট দিয়ে সেটির মান ফেরত পাঠাব।
int root = sqrt(25);
এখানে sqrt ফাংশন 25-এর বর্গমূল নির্ণয় করার পর বর্গমূলটি ফেরত পাঠাবে এবং সেটি root নামের একটি ইন্টিজার ভেরিয়েবলে জমা হবে।
একটি উদাহরণ দিই। তোমরা যারা ত্রিকোণমিতি পড়েছ তারা নিশ্চয়ই sin, cos, tan ইত্যাদির সঙ্গে পরিচিত। sin 300-এর মান হচ্ছে 0.5। এখানে sin কিন্তু আসলে একটি ফাংশন, যার প্যারামিটার হিসেবে আমরা কোণের মান দিচ্ছি। আর ফাংশনটি ওই কোণের sine (সংক্ষেপে sin)-এর মান রিটার্ন করছে।
এবারে চলো, আর বকবক না করে প্রোগ্রামিং শুরু করে দিই। তারপর দেখি কী করলে কী হয়।
#include <stdio.h>
int main()
{
double a, b, c;
a = 2.5;
b = 2.5;
c = a + b;
printf("%lf\n" c);
return 0;
}
প্রোগ্রাম: ৭.১
প্রোগ্রামটি চালাও। আউটপুট কী? 5.000000।
এবার আমরা দুটি সংখ্যা যোগ করার জন্য একটি ফাংশন লিখে ফেলি। যোগের কাজটি আর main ফাংশনের ভেতরে করব না।
#include <stdio.h>
int add(int num1, int num2)
{
double sum = num1 + num2;
return sum;
}
int main()
{
double a, b, c;
a = b = 2.5;
c = add(a, b);
printf("%lf\n", c);
return 0;
}
প্রোগ্রাম: ৭.২
প্রোগ্রামটি চালাও। আউটপুট কী? 4.000000! ওহ্ আমরা তো গাধার মতো একটি ভুল করেছি। num1 ও num2 তো আসলে int টাইপের হবে না, double টাইপের হবে। ওই দুটি ভেরিয়েবল ইন্টিজার হিসেবে ডিক্লেয়ার করার কারণে 2.5 হয়ে গিয়েছে 2 (টাইপ কাস্টিংয়ের কথা মনে আছে তো?)। আমরা ভুল ঠিক করে ফেলি:
int add(double num1, double num2)
{
double sum = num1 + num2;
return sum;
}
এবারে প্রোগ্রামটি রান করলে আউটপুট কী? 5.000000। যাক, সমস্যার সমাধান হয়ে গেল! আচ্ছা, এবারে আমরা a, b-এর মান একটু বদলাই। a = 2.8; b = 2.7; করে দিই। আউটপুট কত হবে? 5.500000? এটিই হওয়া উচিত (2.8 + 2.7 = 5.5) কিন্তু প্রোগ্রামটি রান করে দেখো তো কত হয়? তুমি আউটপুট পাবে 5.000000। কারণ কী?
কারণ, আমাদের ফাংশনের রিটার্ন টাইপ int, যা কিনা একটি ইন্টিজার রিটার্ন করতে সক্ষম। num1 ও num2 যোগ করার পর sum-এর মধ্যে 5.5 ঠিকই থাকবে কিন্তু রিটার্ন করার সময় সেটি ইন্টিজারে বদলে যাবে। সুতরাং রিটার্ন টাইপ আমরা double করে দেব। এবার আমাদের প্রোগ্রাম ঠিকঠাক কাজ করবে:
#include <stdio.h>
double add(double n1, double n2)
{
double sum = n1 + n2;
return sum;
}
int main()
{
double a, b, c;
a = 2.8;
b = 2.7;
c = add(a, b);
printf("%lf\n", c);
return 0;
}
প্রোগ্রাম: ৭.৩
এখন আমরা একটি এক্সপেরিমেন্ট করব। add ফাংশনটি main ফাংশনের পরে লিখব:
#include <stdio.h>
int main()
{
double a = 2.8, b = 2.7, c;
c = add(a, b);
printf("%lf\n", c);
return 0;
}
double add(double n1, double n2)
{
double sum = n1 + n2;
return sum;
}
প্রোগ্রাম: ৭.৪
এবারে প্রোগ্রামটি রান করতে গেলে দেখবে, কম্পাইলার এরর দিচ্ছে: "error: ‘add’ was not declared in this scope", অর্থাৎ সে আর add ফাংশনটিকে চিনতে পারছে না। তবে চিন্তা নেই, এটিকে চিনিয়ে দেওয়ার ব্যবস্থাও আছে। সেটি হচ্ছে main ফাংশনের আগে add ফাংশনের প্রোটোটাইপ (prototype) বলে দেওয়া:
double add(double n1, double n2);
প্রোটোটাইপে পুরা ফাংশনটি লিখতে হয় না। এর অংশগুলো হচ্ছে:
return_type function_name (parameters) ;
সেমিকোলন দিতে ভুল করবে না কিন্তু। আর প্রোটোটাইপের প্যারামিটারে যে ভেরিয়েবল ব্যবহার করবে তার সঙ্গে মূল ফাংশনের ভেরিয়েবলের নাম একরকম না হলে কোনো অসুবিধা নেই, তবে ডাটা টাইপ একই হতে হবে। এখন নিচের প্রোগ্রামটি ঠিকঠাক কাজ করবে:
#include <stdio.h>
double add(double x, double y);
int main()
{
double a = 2.8, b = 2.7, c;
c = add(a, b);
printf("%lf\n", c);
return 0;
}
double add(double n1, double n2)
{
double sum = n1 + n2;
return sum;
}
প্রোগ্রাম: ৭.৫
এবার আমরা আরও কিছু পরীক্ষা-নিরীক্ষা করব।
#include <stdio.h>
int test_function(int x)
{
int y = x;
x = 2 * y;
return (x * y);
}
int main()
{
int x = 10, y = 20, z = 30;
z = test_function(x);
printf("%d %d %d\n", x, y, z);
return 0;
}
প্রোগ্রাম: ৭.৬
প্রোগ্রামটি না চালিয়ে শুধু কোড দেখে বলো তো আউটপুট কী হবে? আমাদের কোনো তাড়া নেই, তাই ধীরেসুস্থে চিন্তা করে বলো।
এবার কে কে আমার সঙ্গে একমত যে আউটপুট হবে:
20 10 200 (অর্থাৎ x = 20, y = 10, z = 200)?
কারণ x, y-এর মান তো test_function-এর ভেতরে আমরা বদলে দিয়েছি। প্রথমে x-এর মান 10 যাচ্ছে প্যারামিটার হিসেবে, তারপরে সেই মানটি আমরা y-তে বসাচ্ছি। মানে y-এর মান এখন 10। তারপর x-এর মান বসাচ্ছি 2 * y মানে 20। তারপর রিটার্ন করছি x * y (যার মান, 20 * 10 বা 200)। সুতরাং z-এর মান হবে 200।
এবারে প্রোগ্রামটি চালাও, আউটপুট দেখবে: 10 20 200 (অর্থাৎ x = 10, y = 20, z = 200)। এমন হওয়ার কারণ কী? z-এর মান নিয়ে কোনো আপত্তি নেই, ফাংশনটি 200 রিটার্ন করে আর সেটি আমরা z-এ বসিয়ে দিয়েছি। কথা হচ্ছে, x আর y-এর মান নিয়ে। আসলে test_function-এর ভেতরে আমরা x, y-এর মান পরিবর্তন করায় main ফাংশনের x, y-এর কিছু আসে-যায় না। প্রত্যেক ফাংশনের ভেরিয়েবলগুলো আলাদা। একে বলে লোকাল ভেরিয়েবল (local variable)। আমরা main ফাংশনের x, y-এর মান প্রিন্ট করেছি test_function ফাংশনের x, y-এর মান প্রিন্ট করিনি। এক ফাংশনের লোকাল ভেরিয়েবলের অস্তিত্ব অন্য ফাংশনে থাকে না। তুমি এখন কিছু প্রোগ্রাম লিখে আরও পরীক্ষা-নিরীক্ষা করে দেখতে পারো। কী প্রোগ্রাম লিখবে সেটি তোমার ওপর ছেড়ে দিলাম।
আমরা যদি চাই, কোনো ভেরিয়েবলের অস্তিত্ব আমাদের প্রোগ্রামের সব ফাংশনের ভেতরে থাকতে হবে, তবে আমরা সেটি করতে পারি গ্লোবাল (global) ভেরিয়েবল ডিক্লেয়ার করার মাধ্যমে। আমরা প্রোগ্রামের শুরুতে কোনো ফাংশন বা ফাংশনের প্রোটোটাইপ লিখার আগে সেগুলো ডিক্লেয়ার করে দেব। যেমন:
এবার কে কে আমার সঙ্গে একমত যে আউটপুট হবে:
20 10 200 (অর্থাৎ x = 20, y = 10, z = 200)?
কারণ x, y-এর মান তো test_function-এর ভেতরে আমরা বদলে দিয়েছি। প্রথমে x-এর মান 10 যাচ্ছে প্যারামিটার হিসেবে, তারপরে সেই মানটি আমরা y-তে বসাচ্ছি। মানে y-এর মান এখন 10। তারপর x-এর মান বসাচ্ছি 2 * y মানে 20। তারপর রিটার্ন করছি x * y (যার মান, 20 * 10 বা 200)। সুতরাং z-এর মান হবে 200।
এবারে প্রোগ্রামটি চালাও, আউটপুট দেখবে: 10 20 200 (অর্থাৎ x = 10, y = 20, z = 200)। এমন হওয়ার কারণ কী? z-এর মান নিয়ে কোনো আপত্তি নেই, ফাংশনটি 200 রিটার্ন করে আর সেটি আমরা z-এ বসিয়ে দিয়েছি। কথা হচ্ছে, x আর y-এর মান নিয়ে। আসলে test_function-এর ভেতরে আমরা x, y-এর মান পরিবর্তন করায় main ফাংশনের x, y-এর কিছু আসে-যায় না। প্রত্যেক ফাংশনের ভেরিয়েবলগুলো আলাদা। একে বলে লোকাল ভেরিয়েবল (local variable)। আমরা main ফাংশনের x, y-এর মান প্রিন্ট করেছি test_function ফাংশনের x, y-এর মান প্রিন্ট করিনি। এক ফাংশনের লোকাল ভেরিয়েবলের অস্তিত্ব অন্য ফাংশনে থাকে না। তুমি এখন কিছু প্রোগ্রাম লিখে আরও পরীক্ষা-নিরীক্ষা করে দেখতে পারো। কী প্রোগ্রাম লিখবে সেটি তোমার ওপর ছেড়ে দিলাম।
আমরা যদি চাই, কোনো ভেরিয়েবলের অস্তিত্ব আমাদের প্রোগ্রামের সব ফাংশনের ভেতরে থাকতে হবে, তবে আমরা সেটি করতে পারি গ্লোবাল (global) ভেরিয়েবল ডিক্লেয়ার করার মাধ্যমে। আমরা প্রোগ্রামের শুরুতে কোনো ফাংশন বা ফাংশনের প্রোটোটাইপ লিখার আগে সেগুলো ডিক্লেয়ার করে দেব। যেমন:
#include <stdio.h>
double pi = 3.14;
void my_fnc() {
pi = 3.1416; /* এখানে আমরা pi-এর মান একটু পরিবর্তন করে দিলাম */
return; /* ফাংশনের রিটার্ন টাইপ void হলে এই return; না দিলেও কিন্তু চলে */
}
int main() {
printf("%lf\n", pi); /* এখানে pi-এর মান হবে 3.14 */
my_fnc();
printf("%lf\n", pi); /* এখানে pi-এর মান হবে 3.1416 কারণ আমরা সেটি my_fnc ফাংশনে গিয়ে বদলে দিয়েছি। */
return 0;
}
আবার আমরা যদি my_fnc ফাংশনের ভেতরে
গিয়ে pi নামে একটি ভেরিয়েবল ডিক্লেয়ার করতাম (double pi;), তবে সেটি একটি
লোকাল ভেরিয়েবল হতো এবং গ্লোবাল pi-এর মানের কোন পরিবর্তন হতো না।
এতক্ষণ আমরা ফাংশনের প্যারামিটার হিসেবে কেবল ভেরিয়েবল ব্যবহার করেছি। এবারে আসো আমরা ফাংশনের প্যারামিটার হিসেবে অ্যারে পাঠাই। আমরা একটি প্রোগ্রাম লিখব যেটি কোনো একটি ইন্টিজার অ্যারে থেকে সবচেয়ে বড় সংখ্যাটি খুঁজে বের করবে। অ্যারে থেকে সর্বোচ্চ সংখ্যা খুঁজে বের করার কাজটি করার জন্য একটি ফাংশন লিখে ফেলি, কী বলো?
int find_max(int ara[], int n) { /* এখানে আমরা দুটি প্যারামিটার দিচ্ছি। প্রথমটা হচ্ছে একটি অ্যারে, আর তারপর একটি সংখ্যা যেটি নির্দেশ করবে অ্যারেতে কয়টি সংখ্যা আছে। লক্ষ করো, প্যারামিটারে যখন অ্যারের কথাটি বলে দিচ্ছি তখন সেখানে কয়টি উপাদান আছে সেটি না দিলেও চলে, যেমন আমরা int ara[11] ও লিখতে পারতাম। */
int max = ara[0]; /* এখানে একটি ভেরিয়েবলে ধরে নিচ্ছি যে সবচেয়ে বড় সংখ্যাটি হচ্ছে অ্যারের প্রথম সংখ্যা। তারপরে আমরা অ্যারের বাকি উপাদানগুলোর সঙ্গে maxকে তুলনা করব আর যদি অ্যারের কোনো উপাদানের মান max-এর চেয়ে বড় হয় তখন সেই মানটি max-এ রেখে দেব। অর্থাৎ তখন আবার max হয়ে যাবে ওই অ্যারের সর্বোচ্চ সংখ্যা। */
int i;
for(i = 1; i < n; i++) { if (ara[i] > max) {
max = ara[i]; /* ara[i] যদি max-এর চেয়ে বড় হয় তবে max-এ ara[i]-এর মানটি অ্যাসাইন করে দিচ্ছি। */
}
}
return max; /* ফাংশন থেকে সর্বোচ্চ মানটি ফেরত পাঠাচ্ছি */
}
এখন কথা হচ্ছে এই ফাংশনকে আমরা কল করব কীভাবে? ভেরিয়েবলের জায়গায় তো এর নাম দিয়ে কল করতে হয়, কিন্তু অ্যারের বেলায় কী দেব? অ্যারের বেলাতেও শুধু নাম দিলেই চলবে। পুরো প্রোগ্রামটি এবারে রান করে দেখো:
এতক্ষণ আমরা ফাংশনের প্যারামিটার হিসেবে কেবল ভেরিয়েবল ব্যবহার করেছি। এবারে আসো আমরা ফাংশনের প্যারামিটার হিসেবে অ্যারে পাঠাই। আমরা একটি প্রোগ্রাম লিখব যেটি কোনো একটি ইন্টিজার অ্যারে থেকে সবচেয়ে বড় সংখ্যাটি খুঁজে বের করবে। অ্যারে থেকে সর্বোচ্চ সংখ্যা খুঁজে বের করার কাজটি করার জন্য একটি ফাংশন লিখে ফেলি, কী বলো?
int find_max(int ara[], int n) { /* এখানে আমরা দুটি প্যারামিটার দিচ্ছি। প্রথমটা হচ্ছে একটি অ্যারে, আর তারপর একটি সংখ্যা যেটি নির্দেশ করবে অ্যারেতে কয়টি সংখ্যা আছে। লক্ষ করো, প্যারামিটারে যখন অ্যারের কথাটি বলে দিচ্ছি তখন সেখানে কয়টি উপাদান আছে সেটি না দিলেও চলে, যেমন আমরা int ara[11] ও লিখতে পারতাম। */
int max = ara[0]; /* এখানে একটি ভেরিয়েবলে ধরে নিচ্ছি যে সবচেয়ে বড় সংখ্যাটি হচ্ছে অ্যারের প্রথম সংখ্যা। তারপরে আমরা অ্যারের বাকি উপাদানগুলোর সঙ্গে maxকে তুলনা করব আর যদি অ্যারের কোনো উপাদানের মান max-এর চেয়ে বড় হয় তখন সেই মানটি max-এ রেখে দেব। অর্থাৎ তখন আবার max হয়ে যাবে ওই অ্যারের সর্বোচ্চ সংখ্যা। */
int i;
for(i = 1; i < n; i++) { if (ara[i] > max) {
max = ara[i]; /* ara[i] যদি max-এর চেয়ে বড় হয় তবে max-এ ara[i]-এর মানটি অ্যাসাইন করে দিচ্ছি। */
}
}
return max; /* ফাংশন থেকে সর্বোচ্চ মানটি ফেরত পাঠাচ্ছি */
}
এখন কথা হচ্ছে এই ফাংশনকে আমরা কল করব কীভাবে? ভেরিয়েবলের জায়গায় তো এর নাম দিয়ে কল করতে হয়, কিন্তু অ্যারের বেলায় কী দেব? অ্যারের বেলাতেও শুধু নাম দিলেই চলবে। পুরো প্রোগ্রামটি এবারে রান করে দেখো:
#include <stdio.h>
int find_max(int ara[], int n);
int main()
{
int ara[] = {-100, 0, 53, 22, 83, 23, 89, -132, 201, 3, 85};
int n = 11;
int max = find_max(ara, n);
printf("%d\n", max);
return 0;
}
int find_max(int ara[], int n)
{
int max = ara[0];
int i;
for(i = 1; i < n; i++) {
if (ara[i] > max) {
max = ara[i];
}
}
return max;
}
প্রোগ্রাম: ৭.৭
এখন তোমরা find_min নামে আরেকটি ফাংশন
লেখো যার কাজ হবে সবচেয়ে ছোট সংখ্যাটি খুঁজে বের করা। find_sum,
find_average এসব ফাংশনও লিখে ফেলতে পারো। আর তোমাদের নিশ্চয়ই বলে দিতে হবে
না এইসব ফাংশন কী কাজ করবে।
ফাংশনে ভেরিয়েবল পাস করা (pass, পাঠানো অর্থে) আর অ্যারে পাস করার মধ্যে একটি গুরুত্বপূর্ণ পার্থক্য রয়েছে। আমরা ইতিমধ্যে দেখেছি যে ফাংশনের ভেতর ভেরিয়েবল পাস করলে ওই ফাংশনের ভেতরে সেটির আরেকটি কপি তৈরি হয়, সুতরাং সেখানে ওই ভেরিয়েবলের মান পরিবর্তন করলে মূল ফাংশন (যেখান থেকে ফাংশন কল করা হয়েছে) ভেরিয়েবলের মানের কোনো পরিবর্তন হয় না। তবে অ্যারের বেলায় ব্যাপারটি আলাদা। আগে আমরা একটি প্রোগ্রাম লিখে দেখি:
ফাংশনে ভেরিয়েবল পাস করা (pass, পাঠানো অর্থে) আর অ্যারে পাস করার মধ্যে একটি গুরুত্বপূর্ণ পার্থক্য রয়েছে। আমরা ইতিমধ্যে দেখেছি যে ফাংশনের ভেতর ভেরিয়েবল পাস করলে ওই ফাংশনের ভেতরে সেটির আরেকটি কপি তৈরি হয়, সুতরাং সেখানে ওই ভেরিয়েবলের মান পরিবর্তন করলে মূল ফাংশন (যেখান থেকে ফাংশন কল করা হয়েছে) ভেরিয়েবলের মানের কোনো পরিবর্তন হয় না। তবে অ্যারের বেলায় ব্যাপারটি আলাদা। আগে আমরা একটি প্রোগ্রাম লিখে দেখি:
#include <stdio.h>
void test_function(int ara[])
{
ara[0] = 100;
return;
}
int main()
{
int ara [] = {1, 2, 3, 4, 5};
printf("%d\n", ara[0]);
test_function(ara);
printf("%d\n", ara[0]);
return 0;
}
প্রোগ্রাম: ৭.৮
এই প্রোগ্রামের আউটপুট কী হবে? প্রথম
printf ফাংশনটি 1 প্রিন্ট করবে সেটি নিয়ে তো কোনো সন্দেহ নেই, কিন্তু
দ্বিতীয় printf কী প্রিন্ট করবে? test_function-এর ভেতর আমরা অ্যারের প্রথম
উপাদানের মান 100 অ্যাসাইন করেছি। এখন যদি সেটি মূল অ্যারেকে পরিবর্তন
করে, তবে ara[0]-এর মান হবে 100, আর পরিবর্তন না হলে মান হবে আগে যা ছিল
তা-ই, মানে 1।
আমরা আউটপুট দেখব 100, কারণ অ্যারেটির প্রথম উপাদানের মান পরিবর্তিত হয়েছে। অর্থাৎ আমরা বুঝতে পারলাম ফাংশনের ভেতরে অ্যারে পাস করলে ওই অ্যারের আলাদা কোনো কপি তৈরি হয় না। কারণ হচ্ছে আমরা ফাংশনের ভেতর অ্যারের নামটি কেবল পাঠাই, যেটি কিনা ওই অ্যারেটি মেমোরির কোন জায়গায় আছে তার অ্যাড্রেস। এখন তোমরা বৃত্তের ক্ষেত্রফল নির্ণয়ের জন্য একটি ফাংশন লিখে ফেলো। ক্ষেত্রফল বের করার সূত্রটি মনে আছে তো? মনে না থাকলে জ্যামিতি বই থেকে দেখে নাও।
আমরা আউটপুট দেখব 100, কারণ অ্যারেটির প্রথম উপাদানের মান পরিবর্তিত হয়েছে। অর্থাৎ আমরা বুঝতে পারলাম ফাংশনের ভেতরে অ্যারে পাস করলে ওই অ্যারের আলাদা কোনো কপি তৈরি হয় না। কারণ হচ্ছে আমরা ফাংশনের ভেতর অ্যারের নামটি কেবল পাঠাই, যেটি কিনা ওই অ্যারেটি মেমোরির কোন জায়গায় আছে তার অ্যাড্রেস। এখন তোমরা বৃত্তের ক্ষেত্রফল নির্ণয়ের জন্য একটি ফাংশন লিখে ফেলো। ক্ষেত্রফল বের করার সূত্রটি মনে আছে তো? মনে না থাকলে জ্যামিতি বই থেকে দেখে নাও।
বাইনারি সার্চ-
একটি
সহজ খেলা দিয়ে শুরু করা যাক। এটি খেলতে দুজন দরকার। একজন মনে মনে একটি
সংখ্যা ধরবে। আর দ্বিতীয়জন কিছু প্রশ্ন করে সেই সংখ্যাটি বের করবে। তবে
'তোমার সংখ্যাটি কত?' - এমন প্রশ্ন কিন্তু সরাসরি করা যাবে না। প্রশ্নটি
হচ্ছে:
সংখ্যাটি কি N (একটি সংখ্যা)-এর চেয়ে বড়, ছোট নাকি সমান?
আর সংখ্যাটি কিন্তু একটি নির্দিষ্ট সীমার মধ্যে হতে হবে (যেমন 1 থেকে 100, 10 থেকে 1000, -1000 থেকে 100000)।
এখন
ধরা যাক, প্রথমজন যে সংখ্যাটি ধরেছে সেটি 1 থেকে 1000-এর ভেতর একটি
সংখ্যা। তাহলে কিন্তু সর্বোচ্চ এক হাজার বার 'সংখ্যাটি কি N-এর সমান?'
প্রশ্নটি করে সেটি বের করে ফেলা যায়। (সংখ্যাটি কি 1? সংখ্যাটি কি 2? ...
সংখ্যাটি কি 999?, সংখ্যাটি কি 1000?)। এভাবে প্রশ্ন করতে থাকলে সংখ্যাটি
অবশ্যই বের হবে। তবে ভাগ্য খারাপ হলে এক হাজার বার ওই প্রশ্নটি করতে হবে।
কিন্তু আমাদের তো এত সময় নেই। ধরা যাক, 1 থেকে 1000-এর ভেতর ওই সংখ্যাটি হচ্ছে 50। তাহলে আমাদের প্রথম প্রশ্ন হবে:
১) সংখ্যাটি কি 500-এর চেয়ে বড়, ছোট নাকি সমান? ছোট।
২) সংখ্যাটি কি 250-এর চেয়ে বড়, ছোট নাকি সমান? ছোট।
৩) সংখ্যাটি কি 125-এর চেয়ে বড়, ছোট নাকি সমান? ছোট।
৪) সংখ্যাটি কি 62-এর চেয়ে বড়, ছোট নাকি সমান? ছোট।
৫) সংখ্যাটি কি 31-এর চেয়ে বড়, ছোট নাকি সমান? বড়।
৬) সংখ্যাটি কি 46-এর চেয়ে বড়, ছোট নাকি সমান? বড়।
৭) সংখ্যাটি কি 54-এর চেয়ে বড়, ছোট নাকি সমান? ছোট।
৮) সংখ্যাটি কি 50-এর চেয়ে বড়, ছোট নাকি সমান? সমান।
আমরা মাত্র আটটি প্রশ্ন করেই সংখ্যাটি পেয়ে গেছি!
তোমরা নিশ্চয়ই পদ্ধতিটি বুঝে
ফেলেছ? প্রতিবার প্রশ্ন করে সংখ্যাটি যে সীমার মধ্যে আছে তাকে অর্ধেক করে
ফেলা হয়েছে। খেলা শুরুর সময় সীমাটি ছিল 1 থেকে 1000। তারপর সেটি হয়েছে 1
থেকে 500। তারপর 1 থেকে 250, 1 থেকে 125, 1 থেকে 62, 31 থেকে 62, 46 থেকে
62, 46 থেকে 54।
সংখ্যা খুঁজে বের করার এই পদ্ধতিকে বলে বাইনারি সার্চ। চলো আমরা তাহলে অ্যালগরিদমটি লিখার চেষ্টা করি:
বাইনারি সার্চ (low, high, N): (শুরুতে আমাদের তিনটি সংখ্যা জানতে হবে, সংখ্যাটির নিম্নসীমা (low), উচ্চসীমা (high) এবং সেই সংখ্যা (N))
ধাপ 1: mid = (low + high) / 2
ধাপ 2: যদি mid এবং N-এর মান সমান হয় তবে ধাপ 5-এ যাও।
ধাপ 3: যদি N, mid-এর চেয়ে বড় হয়, তাহলে low = mid + 1. ধাপ 1-এ যাও।
ধাপ 4: যদি N, mid-এর চেয়ে ছোট হয়, তাহলে high = mid - 1. ধাপ 1-এ যাও।
ধাপ 5: সংখ্যাটি পেয়ে গেছি (mid)।
এখন আমরা দেখব একটি অ্যারে থেকে
কীভাবে বাইনারি সার্চ করে কোনো সংখ্যা খুঁজে বের করতে হয়। অ্যারেতে কিন্তু
সংখ্যাগুলো ছোট থেকে বড় কিংবা বড় থেকে ছোট ক্রমানুসারে থাকতে হবে। নইলে
বাইনারি সার্চ ব্যবহার করা যাবে না। কারণটি কি কেউ বলতে পারো?
প্রথমে আমরা একটি ইন্টিজার অ্যারে নিই যেখানে সংখ্যাগুলো ছোট থেকে বড় ক্রমানুসারে সাজানো আছে।
int ara[] = {1, 4, 6, 8, 9, 11, 14, 15, 20, 25, 33 83, 87, 97, 99, 100};
এখন বলো তো low আর high-এর মান কত
হবে? low = 1 এবং high = 100 ? ঠিকই ধরেছ কিন্তু এখানে একটু সমস্যা আছে।
আমরা এখানে সব সংখ্যার মধ্যে খুঁজব না, বরং অ্যারের ইনডেক্সের মধ্যে
খুঁজব। আর অ্যারের ইনডেক্সগুলো ক্রমানুসারে থাকে বলেই অ্যারেতে বাইনারি
সার্চ করা যায়। এখানে ara-এর সর্বনিম্ন ইনডেক্স হচ্ছে 0 এবং সর্বোচ্চ
ইনডেক্স হচ্ছে 15। তাহলে আমরা দুটি ভেরিয়েবলের মান নির্দিষ্ট করে দিই -
low_indx = 0;
high_indx = 15;
যে সংখ্যাটি খুঁজব ধরা যাক সেটি হচ্ছে 97।
num = 97;
তোমাদের অনেকেই হয়তো ভাবছ, num
সংখ্যাটি যদি ara-তে না থাকে তখন কী হবে? সেটিও আমরা দেখব। সংখ্যাটি যদি
খুঁজে পাওয়া না যায় তবে সেটি জানিয়ে দেওয়ার ব্যবস্থা রাখতে হবে আমাদের
প্রোগ্রামে।
আমাদের যেহেতু খোঁজার কাজটি
বারবার করতে হবে, আমাদেরকে একটি লুপ ব্যবহার করতে হবে। লুপের ভেতর আমরা
খোঁজাখুঁজি করব আর সংখ্যাটি পেয়ে গেলে (কিংবা সংখ্যাটি নেই সেটি নিশ্চিত
হলে) আমরা লুপ থেকে বের হয়ে যাব।
while(1) {
mid_indx = (low_indx + high_indx) / 2;
if(num == ara[mid_indx]) {
/* num যদি ara[mid_indx]-এর সমান হয়, তবে সেটি আমরা পেয়ে গেছি */
break;
}
if(num < ara[mid_indx]) {
/* num যদি ara[mid_indx]-এর ছোট হয়, তবে আমরা low_indx থেকে mid_indx – 1 সীমার মধ্যে খুঁজব। */
high_indx = mid_indx – 1;
}
else {
/* num যদি ara[mid_indx]-এর বড় হয়, তবে আমরা mid_indx + 1 থেকে high_indx সীমার মধ্যে খুঁজব। */
low_indx = mid_indx + 1;
}
}
বাইনারি সার্চের প্রোগ্রাম
আমরা লিখে ফেললাম। খুবই সহজ-সরল প্রোগ্রাম। সংখ্যাটি খুঁজে না পাওয়া
পর্যন্ত লুপটি চলতেই থাকবে, কারণ আমরা লিখেছি while(1) আর 1 সব সময় সত্যি।
কিন্তু সংখ্যাটি যদি ara-তে না থাকে তবে লুপটি চলতেই থাকবে এবং আমাদের
প্রোগ্রাম কখনো বন্ধ হবে না। সুতরাং একটা ব্যবস্থা করা দরকার। আচ্ছা, আমরা
কীভাবে বুঝব যে সংখ্যাটি ara-তে নেই? তোমরা ইতিমধ্যে লক্ষ করেছ যে আমরা
প্রতিবার সার্চের সীমাটা অর্ধেক করে ফেলি। এভাবে চলতে থাকলে একসময় ওই সীমার
ভেতর একটি সংখ্যাই থাকবে। তখন low এবং high-এর মান সমান হবে। আর প্রতিবার
যেহেতু হয় low-এর মান বাড়ছে নাহয় high-এর মান কমছে, সুতরাং যেবার low আর
high সমান হবে, তার পরের বার low-এর মান high-এর মানের চেয়ে বেশি হবে। তখন
আমরা বুঝব যে সংখ্যাটি খুঁজে পাওয়া যায়নি। সুতরাং যতক্ষণ low <= high
ততক্ষণ লুপটি চলবে। লুপ থেকে বের হয়ে যদি দেখি low > high, তখন বুঝব যে
সংখ্যাটি খুঁজে পাওয়া যায়নি, আর না হলে বুঝব সংখ্যাটি খুঁজে পাওয়া গেছে
এবং-এর মান ara[mid_indx]।
তাহলে পুরো প্রোগ্রামটি এবারে লিখে ফেলা যাক:
#include <stdio.h>
int main()
{
int ara[] = {1, 4, 6, 8, 9, 11, 14, 15, 20, 25, 33 83, 87, 97, 99, 100};
int low_indx = 0;
int high_indx = 15;
int mid_indx;
int num = 97;
while (low_indx <= high_indx) {
mid_indx = (low_indx + high_indx) / 2;
if (num == ara[mid_indx]) {
break;
}
if (num < ara[mid_indx]) {
high_indx = mid_indx – 1;
}
else {
low_indx = mid_indx + 1;
}
}
if (low_indx > high_indx) {
printf("%d is not in the array\n", num);
}
else {
printf("%d is found in the array. It is the %d th element of the array.\n", ara[mid_indx], mid_indx);
}
return 0;
}
প্রোগ্রাম: ৮.১
এবার তোমাদের কাজ হবে বাইনারি সার্চের জন্য একটি আলাদা ফাংশন লেখা।
আর বাইনারি সার্চ কীভাবে কাজ করে, সেটি এখানে সুন্দর করে অ্যানিমেশনের মাধ্যমে বোঝানো হয়েছে:
http://video.franklin.edu/Franklin/Math/170/common/mod01/binarySearchAlg.html
আর বাইনারি সার্চ কীভাবে কাজ করে, সেটি এখানে সুন্দর করে অ্যানিমেশনের মাধ্যমে বোঝানো হয়েছে:
http://video.franklin.edu/Franklin/Math/170/common/mod01/binarySearchAlg.html
স্ট্রিং (string)-
তোমরা
যারা string শব্দটির বাংলা অর্থ জানো, তাদের আতঙ্কিত হওয়ার কোনো কারণ নেই,
প্রোগ্রামিংয়ে স্ট্রিং মোটেও দড়ি টানাটানির মতো কষ্টকর ব্যাপার নয়। আবার
তোমাদের মধ্যে যারা একটু জ্ঞানী টাইপের তাদের মাথায় হয়তো স্ট্রিং থিওরী
শব্দটি চলে এসেছে। যা-ই হোক, উদ্বেগের কোনো কারণ নেই।
এক বা একাধিক character মিলে
string তৈরি হয়। সোজা কথায় স্ট্রিং হচ্ছে ক্যারেক্টার টাইপের অ্যারে। তবে
প্রোগ্রামিংয়ে এটির ব্যবহার এতই বেশি যে কোনো কোনো ল্যাঙ্গুয়েজে স্ট্রিংকে
আলাদা একটি ডাটা টাইপ হিসেবে ধরা হয়। তবে সি-তে আমরা char টাইপের অ্যারে
দিয়েই স্ট্রিংয়ের কাজ করব।
নিচের উদাহরণগুলো লক্ষ করো:
char country[11] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', '\0'};
char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', '\0'};
char country[] = "Bangladesh";
char *country = "Bangladesh";
এভাবে আমরা স্ট্রিং ডিক্লেয়ার করতে
পারি। চারটি ডিক্লারেশন আসলে একই জিনিস। সবার শেষে একটি Null character
('\0') দিলে কম্পাইলার বুঝতে পারে এখানেই স্ট্রিংয়ের শেষ। আবার তৃতীয়
উদাহরণে অ্যারের উপাদানগুলো আলাদা করে লেখা হয়নি, একসঙ্গে লেখা হয়েছে। এ
ক্ষেত্রে কম্পাইলার নিজেই Null character বসিয়ে নেবে। চতুর্থ উদাহরণটি একটু
অদ্ভুত। এখানে যে জিনিসটা ব্যবহার করা হয়েছে তার নাম পয়েন্টার (pointer)। এ
বইতে এরকম জিনিস আমরা মাঝে মাঝে ব্যবহার করলেও বিস্তারিত আলোচনায় যাব না।
এবারে প্রোগ্রাম লিখার পালা।
#include <stdio.h>
int main()
{
char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', '\0'};
printf("%s\n", country);
return 0;
}
প্রোগ্রাম: ৯.১
এখানে লক্ষ করো যে printf-এর ভেতরে %s
ব্যবহার করা হয়েছে স্ট্রিং প্রিন্ট করার জন্য। আর অ্যারেতে শেষের '\0'টা
ব্যবহার না করলেও চলে আসলে। বর্তমানের কম্পাইলারগুলো এটি বুঝে নিতে পারে।
#include <stdio.h>
int main()
{
char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', ' ', 'i', 's', ' ', 'm', 'y', ' ', 'c', 'o', 'u', 'n', 't', 'r', 'y'};
printf("%s\n", country);
return 0;
}
প্রোগ্রাম: ৯.২
প্রোগ্রামটি চালাও। তারপর নিচের প্রোগ্রামটি চালাও। আউটপুটে কি পার্থক্য দেখতে পাচ্ছ? পার্থক্যের কারণটা কী?
#include <stdio.h>
int main()
{
char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', '\0', 'i', 's', ' ', 'm', 'y', ' ', 'c', 'o', 'u', 'n', 't', 'r', 'y'};
printf("%s\n", country);
return 0;
}
প্রোগ্রাম: ৯.৩
পার্থক্যটা কী সেটি তোমরা প্রোগ্রাম
দুটি কম্পিউটারে চালালেই বুঝবে। পার্থক্যের কারণ হচ্ছে দ্বিতীয় প্রোগ্রামে
স্ট্রিংয়ের ভেতরে এক জায়গায় '\0' থাকায় কম্পাইলার ধরে নিচ্ছে ওখানে
স্ট্রিংটা শেষ হয়ে গেছে।
এবারে আমরা একটি প্রোগ্রাম লিখব।
একটি স্ট্রিংয়ের ভেতরের সব অক্ষরকে বড় হাতের অক্ষরে (অর্থাৎ capital letter
বা uppercase character) রূপান্তর করা। তবে এর জন্য আমাদের একটি জিনিস
জানতে হবে। প্রতিটি অক্ষরের বিপরীতে কম্পিউটার একটি সংখ্যার কোড ব্যবহার
করে। সেই কোড অনুযায়ী, 'A'-এর মান হচ্ছে 65, 'B'-এর মান হচ্ছে 66, 'C'-এর
মান হচ্ছে 67... এভাবে 'Z'-এর মান হচ্ছে 90। তেমনই 'a' হচ্ছে 97, 'b' হচ্ছে
98 ... এভাবে 'z' হচ্ছে 122। সুতরাং কোনো ক্যারেক্টার বড় হাতের কি না সেটি
আমরা নির্ণয় করতে পারি এভাবে: if(ch >= 'A' && ch <= 'Z')
অথবা if(ch >= 65 && ch <= 90)। তেমনই ছোট হাতের অক্ষরের
জন্য: if(ch >= 'a' && ch <= 'z') অথবা if(ch >= 97
&& ch <= 122)। এখন কোনো ক্যারেক্টার যদি ছোট হাতের হয়, তবে
তাকে বড় হাতের অক্ষরে রূপান্তর করার উপায় কী? উপায় খুব সহজ। একটি উদাহরণ
দেখো: char ch = 'c'; ch = 'A' + (ch – 'a'); এখানে যেটি হচ্ছে, প্রথমে ch
থেকে 'a' বিয়োগ করা হচ্ছে মানে 'c' থেকে 'a' বিয়োগ (আসলে 99 থেকে 97 বিয়োগ
হচ্ছে)। বিয়োগফল 2। এবারে 'A'-এর সঙ্গে যদি ওই 2 যোগ করে দিই তবে সেটি 'C'
হয়ে যাবে! এখন প্রোগ্রামটি লিখে ফেলা যাক:
#include <stdio.h>
int main()
{
char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h'};
int i, length;
printf("%s\n", country);
length = 10;
for(i = 0; i < length; i++) {
if(country[i] >= 97 && country[i] <= 122) {
country[i] = 'A' + (country[i] - 'a');
}
}
printf("%s\n", country);
return 0;
}
প্রোগ্রাম: ৯.৪
এখন তোমরা uppercase থেকে lowercase-এ রূপান্তরের প্রোগ্রামটি লিখে ফেলো। তারপরে আবার বইটি পড়া শুরু করো।
এখানে লক্ষ করো যে স্ট্রিংয়ে
(ক্যারেক্টারের অ্যারেতে) মোট কয়টি উপাদান আছে সেটি আমি দেখেই লিখে ফেলেছি
এবং সরাসরি বসিয়ে দিয়েছি length = 10।
এবার আমরা কোনো স্ট্রিংয়ের
দৈর্ঘ্য মাপার জন্য একটি ফাংশন লিখব! এটি তেমন কঠিন কিছু নয়। একটি লুপের
সাহায্যে স্ট্রিংয়ের প্রতিটি উপাদান পরীক্ষা করতে হবে এবং Null character
('\0') পেলে লুপ থেকে বের হয়ে যাবে অর্থাৎ, '\0' না পাওয়া পর্যন্ত লুপ চলতে
থাকবে। আর লুপ যতবার চলবে স্ট্রিংয়ের দৈর্ঘ্যও তত হবে।
#include <stdio.h>
int string_length(char str[])
{
int i, length = 0;
for(i = 0; str[i] != '\0'; i++) {
length++;
}
return length;
}
int main()
{
char country[100];
int length;
while(1 == scanf("%s", country)) {
length = string_length(country);
printf("length: %d\n", length);
}
return 0;
}
প্রোগ্রাম: ৯.৫
ওপরের প্রোগ্রামটায় তোমরা দেখতে পাচ্ছ
যে ইনপুট নেওয়ার জন্য scanf ফাংশন ব্যবহার করা হয়েছে এবং স্ট্রিং ইনপুট
নেওয়ার জন্য %s ব্যবহৃত হয়েছে। scanf ফাংশনটি যতটি উপাদান ইনপুট হিসেবে
নেয়, সেই সংখ্যাটি রিটার্ন করে। সাধারণত রিটার্ন ভ্যালুটি আমাদের দরকার হয়
না, তাই scanf ব্যবহার করলেও আমরা ওই ভ্যালুটি রাখি না। যেমন দুটি ইন্টিজার
ইনপুট নিতে গেলে আমরা লিখি: scanf("%d %d", &n1, &n2);। আমরা এটি
চাইলে এভাবেও লিখতে পারতাম: value = scanf("%d %d", &n1, &n2);।
তোমরা প্রিন্ট করলে দেখবে value-এর মান 2। while(1 == scanf("%s",
country)) লাইনে যেটি ঘটছে তা হলো, যতক্ষণ একটি country-এর নাম scanf দিয়ে
ইনপুট নেওয়া হচ্ছে, ফাংশনটি 1 রিটার্ন করছে, আর লুপের ভেতরের কন্ডিশন সত্য
হচ্ছে (1 == 1), তাই লুপের কাজ চলতে থাকবে।
আরেকটি জিনিস খেয়াল করো যে country-এর
আগে কোন & চিহ্ন ব্যবহার করা হয়নি। তোমরা &country লিখে দেখো
প্রোগ্রামটি কী আচরণ করে। তবে %s ব্যবহারের একটি সমস্যা হচ্ছে স্ট্রিংয়ে
কোনো হোয়াইটস্পেস ক্যারেক্টার (যেমন: স্পেস, ট্যাব ইত্যাদি) থাকা যাবে না,
এমন কিছু পেলে scanf ওই ক্যারেক্টার পর্যন্ত একটি স্ট্রিং ধরে নেয়। যেমন,
ইনপুট যদি হয় this is তবে scanf প্রথমে thisকেই স্ট্রিং হিসেবে নেবে,
তারপরে যদি আবার scanf ফাংশন কল করা হয়, তবে isকে সে স্ট্রিং হিসেবে ইনপুট
নিয়ে নেবে। এই সমস্যা এড়ানোর জন্য আমরা gets ফাংশন ব্যবহার করতে পারি।
নিচের উদাহরণটি দেখো:
#include <stdio.h>
int main()
{
char ara[100];
while(NULL != gets(ara)) {
printf("%s\n", ara);
}
return 0;
}
প্রোগ্রাম: ৯.৬
এই প্রোগ্রামটিও চলতে থাকবে যতক্ষণ না
তুমি ctrl + z (মানে কি-বোর্ডে ctrl ও z একসঙ্গে) চাপো, লিনাক্সের জন্য
ctrl + d। ctrl + z বা ctrl + d দিলে gets ফাংশনটি NULL রিটার্ন করে।
আরেকটি জিনিস লক্ষ করো যে আমি char ara[100]; ডিক্লেয়ার করে শুরুতেই বলে
দিয়েছি স্ট্রিংয়ের সর্বোচ্চ দৈর্ঘ্য হবে 100।
আরেকটি ব্যাপার। string_length ফাংশনের ভেতরে আসলে দুটি ভেরিয়েবল ব্যবহার না করলেও চলে। আমরা ফাংশনটি এভাবেও লিখতে পারি:
int string_length(char str[])
{
int i;
for(i = 0; str[i] != '\0'; i++);
return i;
}
এখন তোমাদের কাজ হবে string_length ফাংশনটি for লুপ ব্যবহার না করে while লুপ ব্যবহার করে লেখা।
আমাদের পরবর্তী প্রোগ্রামের
লক্ষ্য হবে দুটি স্ট্রিং জোড়া দেওয়া বা concatenate করা। যেমন একটি স্ট্রিং
যদি হয় "bangla" এবং আরেকটি স্ট্রিং যদি হয় "desh" তবে দুটি জোড়া দিয়ে
"bangladesh" বানাতে হবে।
প্রথমেই স্ট্রিংগুলো ডিক্লেয়ার করে নেই: char str1[] = "bangla", str2[] = "desh", str3[12];
আমাদের লক্ষ হচ্ছে str3তে "bangladesh" রাখা। খুব সুবিধা হতো যদি আমরা এমন কিছু লিখতে পারতাম:
str3 = str1 + str2;
কিন্তু 'সি'-তে এভাবে দুটি অ্যারে
বা স্ট্রিং যোগ করা যায় না। তাই একটি একটি করে str1-এর উপাদানগুলো str3তে
কপি করতে হবে, তারপর str2-এর উপাদানগুলো str3তে কপি করতে হবে।
#include <stdio.h>
int main()
{
char str1[] = "bangla", str2[] = "desh", str3[12];
int i, j, length1 = 6, length2 = 4;
for(i = 0, j = 0; i < length1; i++, j++) {
str3[j] = str1[i];
}
for(i = 0, j = 0; i < length2; i++, j++) {
str3[j] = str2[i];
}
str3[j] = '\0';
printf("%s\n", str3);
return 0;
}
প্রোগ্রাম: ৯.৭
প্রোগ্রামটি চালাও। আউটপুট কী আসা উচিত?
bangladesh। কিন্তু আউটপুট এসেছে desh। আসলে আমরা কিছু একটা ভুল করেছি।
তোমাদের এখন সেই ভুলটি ঠিক করার চেষ্টা করা উচিত। অন্তত তিরিশ মিনিট
চেষ্টার পরও যদি ভুল বের করতে না পারো তবে আবার বইটি পড়া শুরু করো।
for(i = 0, j = 0; i < length1; i++, j++) {
str3[j] = str1[i];
}
এখানে আমরা শুরুতেই i-এর মান 0 করেছি
কারণ iকে আমরা str1-এর ইনডেক্স হিসেবে ব্যবহার করব। jকে ব্যবহার করব
str3-এর ইনডেক্স হিসেবে তাই j-এর মানও 0 করা হয়েছে। তারপর একে একে str1-এর
উপাদানগুলো str3তে কপি করছি এবং i ও j-এর মান 1 করে বাড়াচ্ছি (i++, j++)।
লুপ শেষ হওয়ার পরে i ও j প্রত্যেকের মান হবে 6।
এখন পরের লুপে আমরা str2কে
str3-তে কপি করব। এখন str2-এর ইনডেক্স হিসেবে যদি i ব্যবহার করি, তবে তার
মান লুপের শুরুতেই আবার 0 করে দিতে হবে। আমরা সেটি করেছি। কিন্তু ভুল করেছি
সেই সঙ্গে j-এর মান 0 করে দিয়ে। j-এর মান 0 করলে তো str2-এর প্রথম (0তম)
উপাদান str3-এর প্রথম (0তম) উপাদান হিসেবে কপি হবে, কিন্তু আমরা তো সেটি
চাই না। আমরা চাই str2-এর প্রথম উপাদান হবে str3-এর সপ্তম উপাদান। তাহলে
j-এর মান 0 করা যাবে না। তাই দ্বিতীয় লুপটি হবে এমন:
for(i = 0; i < length2; i++, j++) {
str3[j] = str2[i];
}
আরেকটি ব্যাপার লক্ষ করো। দ্বিতীয় লুপ
থেকে বের হবার পরে str3-এর শেষ ঘরে '\0' অ্যাসাইন করেছি (str3[j] = '\0';)
যাতে স্ট্রিংটা যে ওখানেই শেষ, এটি কম্পাইলার বুঝতে পারে।
আমাদের পরবর্তী প্রোগ্রাম হবে
দুটি স্ট্রিংয়ের মধ্যে তুলনা করা। অর্থাৎ দুটি স্ট্রিংয়ের মধ্যে ছোট, বড়,
সমান নির্ণয় করা। সংখ্যার ক্ষেত্রে যেমন >, <, >=, <=, ==
চিহ্ন ব্যবহার করে তুলনা করা যায়, স্ট্রিংয়ের ক্ষেত্রে সেই ব্যবস্থা নাই।
কিন্তু স্ট্রিংয়ের ক্ষেত্রে প্রায়ই আমাদের এই তুলনা করার দরকার পড়বে। যেমন
ধরো, সর্টিংয়ের ক্ষেত্রে যেখানে ছোট থেকে বড় বা বড় থেকে ছোট ক্রমানুসারে
সাজাতে হবে (alphabetical ordering)। স্ট্রিংয়ে ছোট-বড় আবার কী? বেশি কথা
বলে ব্যাখ্যা না করে কিছু উদাহরণ দিই, তাহলেই বুঝতে পারবে। 'aaa'-এর চেয়ে
'aab' বড়। আবার 'ba' ও 'ca'-এর মধ্যে 'ca' বড়। এই প্রোগ্রামে আমরা একটি
ফাংশন লিখব string_compare() যেটির কাজ হবে দুটি স্ট্রিংয়ের মধ্যে তুলনা
করে প্রথমটি দ্বিতীয়টির চেয়ে বড় হলে 1 রিটার্ন করবে, ছোট হলে -1 আর দুটি
সমান হলে 0 রিটার্ন করবে। ফাংশনের রিটার্ন টাইপ হবে ইন্টিজার এবং
প্যারামিটার হবে দুটি char টাইপের অ্যারে।
int string_compare(char a[], char b[])
{
}
আমাদের মূল কাজ হবে a-এর প্রথম উপাদানের
সঙ্গে b-এর প্রথম উপাদান, a-এর দ্বিতীয় উপাদানের সঙ্গে b-এর দ্বিতীয়
উপাদান এভাবে তুলনা করতে থাকা। যখনই a-এর কোনো উপাদান b-এর কোনো উপাদানের
চেয়ে ছোট হবে, আমরা সঙ্গে সঙ্গে বলে দিতে পারি যে a, b-এর চেয়ে ছোট। সুতরাং
-1 রিটার্ন করে ফাংশন থেকে বের হয়ে আসব। একইভাবে যখনই a-এর কোনো উপাদান
b-এর কোনো উপাদানের চেয়ে বড় হবে, সঙ্গে সঙ্গে 1 রিটার্ন করে ফাংশন থেকে বের
হয়ে আসব কারণ a, b-এর চেয়ে বড়। কিন্তু যদি সবগুলোই সমান হয়? তখন আমরা 0
রিটার্ন করব। তাতে বুঝব যে স্ট্রিং দুটি সমান।
int string_compare(char a[], char b[])
{
int i, j;
for(i = 0; a[i] != '\0' && b[i] != '\0'; i++) {
if(a[i] < b[i]) {
return -1;
}
if(a[i] > b[i]) {
return 1;
}
}
if(string_length(a) == string_length(b)) {
return 0;
}
if(string_length(a) < string_length(b)) {
return -1;
}
if(string_length(a) > string_length(b)) {
return 1;
}
}
স্ট্রিংয়ের বেসিক জিনিসগুলো নিয়ে আলোচনা
করলাম। তবে মজার ব্যাপার হচ্ছে সি ল্যাঙ্গুয়েজে একটি হেডার ফাইল আছে, যার
নাম string.h এবং ওইখানে বেশিরভাগ স্ট্রিং-সংক্রান্ত কাজের জন্য ফাংশন তৈরি
করে দেওয়া আছে (যেমন: strcmp, strlen, strcpy ইত্যাদি)। তোমাদের দিয়ে
কাজগুলো আমি আবার করালাম বলে দুঃখ পাওয়ার কোনো কারণ নেই, আমার ওপর রাগ
করারও কিছু নেই। মৌলিক জিনিসগুলো শিখে রাখা সব সময়ই গুরুত্বপূর্ণ, যা তোমার
প্রোগ্রামিং চিন্তাকে বিকশিত করবে।
এখন আমরা আরেকটি প্রোগ্রাম লিখব যেটি
ইনপুট হিসেবে একটি স্ট্রিং নেবে (যেখানে অনেকগুলো শব্দ থাকবে)। এই
স্ট্রিংয়ের সর্বোচ্চ দৈর্ঘ্য হবে 1000। শব্দগুলোর মাঝখানে এক বা একাধিক
স্পেস থাকবে। আউটপুট হিসেবে প্রতিটি শব্দ আলাদা লাইনে প্রিন্ট করতে হবে।
বিরামচিহ্নগুলো (punctuation) প্রিন্ট করা যাবে না এবং শব্দের প্রথম অক্ষর
হবে বড় হাতের।
অনেক শর্ত দিয়ে ফেললাম। তবে প্রোগ্রামটি
খুব কঠিন কিছু নয়। নিজে নিজে চেষ্টা করতে পারো। আর না পারলে এখন চলো দেখি
কীভাবে সমাধান করা যায়।
প্রথম কথা হচ্ছে, ইনপুট নেব
কীভাবে? বুঝতেই পারছ যে ইনপুটে যেহেতু স্পেস থাকবে, scanf("%s") ব্যবহার
করা যাবে না। তাই আমরা gets() ব্যবহার করব। তার পরের কথা হচ্ছে একটি শব্দে
কোন কোন ক্যারেক্টার থাকতে পারে? যেহেতু বলা নেই, আমরা ধরে নিই 'a' থেকে
'z', 'A' থেকে 'Z' আর '0' থেকে '9' থাকবে।
তার পরের প্রশ্ন হচ্ছে, আমরা কখন বুঝব
বা আমাদের প্রোগ্রামকে কীভাবে বোঝাবো যে একটি শব্দ শুরু হয়েছে?-এর জন্য
আমরা একটি ভেরিয়েবল রাখতে পারি। ভেরিয়েবলের নাম যদি দিই is_word_started
তাহলে এর মান 0 হলে বুঝব শব্দ শুরু হয়নি, শব্দ শুরু হলে এর মান আমরা 1 করে
দেব। আবার শব্দ শেষ হলে 0 করে দেব। যখন দেখব শব্দ শুরু হয়ে গেছে
(is_word_started-এর মান 1) কিন্তু কোনো ক্যারেক্টারের মান 'a' – 'z' বা
'A' – 'Z', বা '0' – '9' এই রেঞ্জের মধ্যে নেই, তখনই বুঝব শব্দটি শেষ।
তোমরা যদি এর আগে প্রোগ্রামটি চেষ্টা করার পরও লিখতে না পারো, এখন চেষ্টা
করলে পারবে আশা করি। আমি এখন কোডটি লিখে দেব তবে সেটি দেখার আগে অবশ্যই
নিজে করার চেষ্টা করতে হবে।
#include <stdio.h>
#include <string.h>
int main()
{
char s[1002], word[100];
int i, j, length, is_word_started;
gets(s);
length = strlen(s);
is_word_started = 0;
for (i = 0, j = 0; i < length; i++) {
if (s[i] >= 'a' && s[i] <= 'z') {
if (is_word_started == 0) {
is_word_started = 1;
word[j] = 'A' + s[i] - 'a'; // first character is capital
j++;
}
else {
word[j] = s[i];
j++;
}
}
else if (s[i] >= 'A' && s[i] <= 'Z') {
if (is_word_started == 0) {
is_word_started = 1;
}
word[j] = s[i];
j++;
}
else if (s[i] >= '0' && s[i] <= '9') {
if (is_word_started == 0) {
is_word_started = 1;
}
word[j] = s[i];
j++;
}
else {
if (is_word_started == 1) {
is_word_started = 0;
word[j] = '\0';
printf("%s\n", word);
j = 0;
}
}
}
return 0;
}
প্রোগ্রাম: ৯.৮
প্রোগ্রামটি বুঝতে কি একটু সমস্যা
হচ্ছে? সে পরে দেখা যাবে, আগে প্রোগ্রামটি চটপট কম্পিউটারে টাইপ করে ফেলো,
কম্পাইল ও রান করো। যারা লিনাক্স ব্যবহার করছ তারা gets() ব্যবহারের কারণে
কম্পাইলার থেকে একটি সতর্ক সংকেত (warning) পেতে পারো, পাত্তা দিয়ো না।
ইনপুট হিসেবে যেকোনো কিছু লিখতে পারো। যেমন: This is a test.। আউটপুট কী?
আউটপুট হচ্ছে এই রকম:
This
Is
A
কী মুশকিল! test গেল কোথায়?
এখন তোমার কাজ হবে test-এর নিখোঁজ হওয়ার রহস্যটা তদন্ত করা। তারপর আমি প্রোগ্রামটি ব্যাখ্যা করব।
তোমরা দেখো প্রোগ্রামে আমি
স্ট্রিংয়ের দৈর্ঘ্য নির্ণয়ের জন্য strlen ফাংশন ব্যবহার করেছি। আর-এর জন্য
আমাকে string.h হেডার ফাইলটি include করতে হয়েছে। ইনপুট হিসেবে স্ট্রিংটা
নিলাম s-এ। আর word রাখার জন্য একটি অ্যারে ডিক্লেয়ার করে রেখেছি। তারপর
আমি i = 0 থেকে length পর্যন্ত একটি লুপ চালিয়েছি s-এর ভেতরের প্রতিটি
ক্যারেক্টার পরীক্ষা করার জন্য।
if (s[i] >= 'a' && s[i]
<= 'z') দিয়ে পরীক্ষা করলাম এটি ছোট হাতের অক্ষর নাকি। যদি ছোট হাতের
অক্ষর হয় তবে একটি শব্দের প্রথম অক্ষর কি না সেটি জানতে হবে। কারণ প্রথম
অক্ষর হলে ওটাকে আবার বড় হাতের অক্ষরে রূপান্তর করতে হবে। সেই পরীক্ষাটা
আমরা করেছি: if (is_word_started == 0) দিয়ে। এটি সত্য হওয়া মানে শব্দ শুরু
হয়নি, এটিই প্রথম অক্ষর। তাই আমরা is_word_started-এর মান 1 করে দেব।
আর word[j]তে s[i]-এর বড় হাতের অক্ষরটা নেব। তারপর j-এর মান এক বাড়াতে হবে।
else if (s[i] >= 'A' && s[i] <= 'Z') এবং else if (s[i]
>= '0' && s[i] <= '9') এই দুটি শর্তের ভেতরেই আমরা একই কাজ
করি। s[i]কে word[j]তে কপি করি। তাই চাইলে দুটি শর্তকে একসঙ্গে এভাবেও
লিখতে পারতাম: else if ((s[i] >= 'A' && s[i] <= 'Z') ||
(s[i] >= '0' && s[i] <= '9')) তার পরের else-এর ভেতরে
ঢোকার মানে হচ্ছে আগের if এবং else if-এর শর্তগুলো মিথ্যা হয়েছে। তাই
s[i]-এর ভেতরে যেই ক্যারেক্টার আছে সেটি word-এ রাখা যাবে না। এবং যদি word
ইতিমধ্যে শুরু হয়ে গিয়ে থাকে, সেটি শেষ করতে হবে এবং wordটি প্রিন্ট করতে
হবে। আর যদি word শুরু না হয়ে থাকে তাহলে কিছু করার দরকার নেই।
else {
if (is_word_started == 1) {
is_word_started = 0;
word[j] = '\0';
printf("%s\n", word);
j = 0;
}
}
তোমরা কি test-রহস্য সমাধান করতে পেরেছ?
তোমরা চেষ্টা করতে থাকো আর আমি এখন প্রোগ্রামটি অন্যভাবে লিখব (এর সঙ্গে
test রহস্যের কোনো সম্পর্ক নেই সেটি বলে রাখলাম)।
এখন আমি যেটি করব, প্রোগ্রামটি
এমনভাবে লিখব যাতে word অ্যারেটিই ব্যবহার করতে না হয়! একটু চিন্তা করে
দেখো। আসলে তো এই অ্যারেটি নিয়ে আমরা কিছু করছি না প্রিন্ট করা ছাড়া। তাই
এর আসলে কোনো দরকার নেই।
#include <stdio.h>
#include <string.h>
int main()
{
char s[1002], ch;
int i, length, is_word_started;
gets(s);
length = strlen(s);
is_word_started = 0;
for (i = 0; i < length; i++) {
if (s[i] >= 'a' && s[i] <= 'z') {
if (is_word_started == 0) {
is_word_started = 1;
ch = 'A' + s[i] - 'a';
printf("%c", ch);
}
else {
printf("%c", s[i]);
}
}
else if ((s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= '0' && s[i] <= '9')) {
if (is_word_started == 0) {
is_word_started = 1;
}
printf("%c", s[i]);
}
else {
if (is_word_started == 1) {
is_word_started = 0;
printf("\n");
}
}
}
printf("\n");
return 0;
}
প্রোগ্রাম: ৯.৯
এখন প্রোগ্রামটি বুঝতে চেষ্টা করো এবং বিভিন্ন ইনপুট দিয়ে পরীক্ষা করে দেখো। যেমন:
This is test number 9.9
স্ট্রিং-সংক্রান্ত সমস্যাগুলো দেখতে জটিল মনে হলেও আসলে সহজ। আর এ ধরনের সমস্যা সমাধানের যত চর্চা করবে দক্ষতা তত বাড়বে।
মৌলিক সংখ্যা-
মৌলিক
সংখ্যা (Prime Number) গণিতবিদদের কাছে যেমন প্রিয়, তেমনই প্রোগ্রামারদেরও
অনেক প্রিয় একটি বিষয়। তোমাদের বিভিন্ন সময়ে এই মৌলিক সংখ্যাসংক্রান্ত
নানা সমস্যার সমাধান করতে হবে। মৌলিক সংখ্যা জিনিসটি যে গুরুত্বপূর্ণ সেটি
বোঝার আরেকটি উপায় হলো, এই বইতে বিষয়টির জন্য আমি একটি পৃথক অধ্যায় বরাদ্দ
করেছি। মৌলিক সংখ্যা হচ্ছে সেসব সংখ্যা যারা 1-এর চেয়ে বড় পূর্ণসংখ্যা এবং
সেটি কেবল 1 এবং ওই সংখ্যাটি দ্বারাই নিঃশেষে বিভাজ্য হবে। খুবই সহজ-সরল
জিনিস। এখন কোনো সংখ্যা মৌলিক কি না সেটি বের করার জন্য একটি প্রোগ্রাম
লিখে ফেলা যাক।
#include <stdio.h>
int is_prime(int n)
{
int i;
if (n < 2) {
return 0;
}
for(i = 2; i < n; i++) {
if(n % i == 0) {
return 0;
}
}
return 1;
}
int main()
{
int n;
while(1) {
printf("Please enter a number (enter 0 to exit): ");
scanf("%d", &n);
if(n == 0) {
break;
}
if(1 == is_prime(n)) {
printf("%d is a prime number.\n", n);
}
else {
printf("%d is not a prime number.\n", n);
}
}
return 0;
}
প্রোগ্রাম: ১০.১
মৌলিক সংখ্যা নির্ণয়ের
জন্য আমরা একটি ফাংশন লিখেছি যেটির প্যারামিটার হচ্ছে একটি ইন্টিজার নম্বর
n। ফাংশনে আমরা nকে 2 থেকে n-1 পর্যন্ত সংখ্যাগুলো দিয়ে ভাগ করার চেষ্টা
করেছি একটি লুপের সাহায্যে। যদি এর মধ্যে কোনো সংখ্যা দিয়ে n নিঃশেষে
বিভাজ্য হয়, তবে আমরা সঙ্গে সঙ্গেই বলে দিতে পারি যে সেটি মৌলিক সংখ্যা নয়
এবং ফাংশনটি 0 রিটার্ন করে। আর যদি সব সংখ্যা দিয়ে ভাগ করার পরও দেখা যায়
যে কোন সংখ্যাই nকে নিঃশেষে ভাগ করতে পারেনি, তখন আমরা এই সিদ্ধান্তে আসতে
পারি যে n একটি মৌলিক সংখ্যা। আর তখন ফাংশন থেকে 1 রিটার্ন করি। আমরা মৌলিক
সংখ্যা নির্ণয় করা শিখে গেলাম! আমি প্রোগ্রামটি লিখার সময় যে পথ অবলম্বন
করেছি সেটি হচ্ছে খুব সহজ-সরল পথ। প্রোগ্রামটিকে মোটেও ইফিশিয়েন্ট
(efficient) বানানোর চেষ্টা করিনি। তোমরা খুব সহজেই ব্যাপারটি বুঝতে পারবে।
প্রোগ্রামে ইনপুট হিসেবে 2147483647 দাও। এটি যে মৌলিক সংখ্যা সেটি বের
করতে বেশ সময় লাগে। কারণ তখন 2147483647কে 2 থেকে 2147483646 পর্যন্ত সব
সংখ্যা দিয়ে ভাগ করার ব্যর্থ চেষ্টা করা হয়। প্রোগ্রামটিকে আরও ইফিশিয়েন্ট
করতে হবে।
একটি বুদ্ধি তোমাদের মাথায় এর মধ্যেই নিশ্চয়ই এসে গেছে। সেটি হচ্ছে 2 থেকে n-1 পর্যন্ত সব সংখ্যা দিয়ে ভাগ করার চেষ্টা না করে 2 থেকে n/2 পর্যন্ত সংখ্যাগুলো দিয়ে ভাগ করার চেষ্টা করলেই হয়। তাহলে প্রোগ্রামের গতি দ্বিগুণ হয়ে যাবে। এখন তোমরা আরেকটি বিষয় লক্ষ করো। কোন সংখ্যা যদি 2 দিয়ে নিঃশেষে বিভাজ্য না হয়, তবে সেটি অন্য কোন জোড় সংখ্যা দিয়ে নিঃশেষে বিভাজ্য হওয়ার প্রশ্নই আসে না। তাই 2 বাদে অন্য জোড় সংখ্যাগুলো (4, 6, 8, …) দিয়ে ভাগ করার চেষ্টা করাটা আসলে বোকামি। জোড় সংখ্যা দিয়ে বিভাজ্যতার পরীক্ষাটা আমরা ফাংশনের শুরুতেই করে নিতে পারি। এখন আমাদের ফাংশনটির চেহারা দাঁড়াবে এই রকম:
একটি বুদ্ধি তোমাদের মাথায় এর মধ্যেই নিশ্চয়ই এসে গেছে। সেটি হচ্ছে 2 থেকে n-1 পর্যন্ত সব সংখ্যা দিয়ে ভাগ করার চেষ্টা না করে 2 থেকে n/2 পর্যন্ত সংখ্যাগুলো দিয়ে ভাগ করার চেষ্টা করলেই হয়। তাহলে প্রোগ্রামের গতি দ্বিগুণ হয়ে যাবে। এখন তোমরা আরেকটি বিষয় লক্ষ করো। কোন সংখ্যা যদি 2 দিয়ে নিঃশেষে বিভাজ্য না হয়, তবে সেটি অন্য কোন জোড় সংখ্যা দিয়ে নিঃশেষে বিভাজ্য হওয়ার প্রশ্নই আসে না। তাই 2 বাদে অন্য জোড় সংখ্যাগুলো (4, 6, 8, …) দিয়ে ভাগ করার চেষ্টা করাটা আসলে বোকামি। জোড় সংখ্যা দিয়ে বিভাজ্যতার পরীক্ষাটা আমরা ফাংশনের শুরুতেই করে নিতে পারি। এখন আমাদের ফাংশনটির চেহারা দাঁড়াবে এই রকম:
int is_prime(int n)
{
int i;
if (n < 2) {
return 0;
}
if(n == 2) {
return 1;
}
if(n % 2 == 0) {
return 0;
}
for(i = 3; i <= n / 2; i = i + 2) {
if(n % i == 0) {
return 0;
}
}
return 1;
}
প্রথমে আমরা পরীক্ষা করেছি
n-এর মান 2 কি না। যদি 2 হয় তবে বলে দিয়েছি যে n মৌলিক সংখ্যা। তারপরে
আমরা পরীক্ষা করেছি n জোড় সংখ্যা কি না। যদি জোড় হয়, তবে n মৌলিক সংখ্যা
না, কেবল 2ই একমাত্র জোড় মৌলিক সংখ্যা যেটির পরীক্ষা আমরা একেবারে শুরুতেই
করে ফেলেছি। তারপর আমরা 3 থেকে n / 2 পর্যন্ত সব বেজোড় সংখ্যা দিয়ে nকে ভাগ
করার চেষ্টা করেছি। এখন তোমরা বিভিন্ন ইনপুট দিয়ে প্রোগ্রামটি পরীক্ষা করে
দেখো। 2147483647 দিয়ে পরীক্ষা করলে বুঝতে পারবে যে প্রোগ্রামের গতি আগের
চেয়ে বেড়েছে কিন্তু তার পরও একটু সময় লাগছে। আমার কম্পিউটারে চার সেকেন্ডের
মতো সময় লাগছে। কিন্তু এত সময় তো দেওয়া যাবে না। তোমাদের যাদের গাণিতিক
বুদ্ধিশুদ্ধি বেশি, তারা একটু চিন্তা করলেই প্রোগ্রামটির গতি বাড়ানোর একটি
উপায় বের করে ফেলতে পারবে। সেটি হচ্ছে n-এর উৎপাদক বের করার জন্য আসলে n / 2
পর্যন্ত সব সংখ্যা দিয়ে পরীক্ষা করার দরকার নেই। n-এর বর্গমূল পর্যন্ত
পরীক্ষা করলেই হয়। n = p x q হলে, p বা q যেকোনো একটি সংখ্যা অবশ্যই n-এর
বর্গমূলের সমান বা তার ছোট হবে। বর্গমূল নির্ণয়ের জন্য আমরা math.h হেডার
ফাইলের sqrt() ফাংশনটি ব্যবহার করব। আমাদের প্রোগ্রামটি দাঁড়াচ্ছে এই রকম:
#include <stdio.h>
#include <math.h>
int is_prime(int n)
{
int i, root;
if(n == 2) {
return 1;
}
if(n % 2 == 0) {
return 0;
}
root = sqrt(n);
for(i = 3; i <= root; i = i + 2) {
if(n % i == 0) {
return 0;
}
}
return 1;
}
int main()
{
int n, m;
while(1) {
printf("Please enter a number (enter 0 to exit): ");
scanf("%d", &n);
if(n == 0) {
break;
}
if(1 == is_prime(n)) {
printf("%d is a prime number.\n", n);
}
else {
printf("%d is not a prime number.\n", n);
}
}
return 0;
}
প্রোগ্রাম: ১০.২
এখন তোমরা প্রোগ্রামটি
চালিয়ে বিভিন্ন ইনপুট দিয়ে পরীক্ষা করে দেখো। একটি কথা বলে দিই।
প্রোগ্রামটায় একটি বাগ আছে (মানে ভুল আছে)। সেটি খুঁজে বের করে ঠিক করে
ফেলো।
প্রাইম নম্বর বের করতে পেরে তোমরা নিশ্চয়ই বেশ খুশি? কিন্তু আমাদের চেষ্টা এখানেই থেমে থাকবে না। আমরা এখন দেখব আরেকটি চমৎকার পদ্ধতি, গ্রিক গণিতবিদ ইরাতোসথেনেস (Eratosthenes) আজ থেকে দুই হাজার বছরেরও আগে এই পদ্ধতি আবিষ্কার করেছিলেন। এজন্য-এর নাম হচ্ছে সিভ অব ইরাতোসথেনেস (Sieve of Eratosthenes)।
পদ্ধতিটি ব্যাখ্যা করা যাক। ধরো, আমরা 2 থেকে 40 পর্যন্ত সব মৌলিক সংখ্যা বের করব। শুরুতে সব সংখ্যা লিখে ফেলি: 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 , 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40. এখন দেখো, তালিকার প্রথম সংখ্যা হচ্ছে 2। এবারে 2-এর সব গুণিতক (2 বাদে, মানে 2-এর চেয়ে বড়গুলো আরকী) বাদ দিয়ে দাও। তাহলে থাকবে: 2, 3, 5, 7, 9, 11, 13, 15, 17, 19 , 21, 23, 25, 27, 29, 31, 33, 35, 37, 39. এখন তালিকার দ্বিতীয় সংখ্যা 3-এর সব গুণিতক (3-এর চেয়ে বড়গুলো) বাদ দাও। 2, 3, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37. এখন তালিকার তৃতীয় সংখ্যা 5-এর সব গুণিতক (5 বাদে) বাদ দাও। 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37. পরবর্তী সংখ্যা হচ্ছে 7 কিন্তু সেটির গুণিতক খোঁজার চেষ্টা করা বৃথা। কারণ তালিকার সর্বোচ্চ সংখ্যা 37-এর বর্গমূল 7-এর চেয়ে ছোট। সুতরাং 7-এর যে গুণিতকগুলো তালিকায় ছিল সেগুলো ইতিমধ্যে তালিকা থেকে বাদ পড়েছে। কারণটি বুঝতে সমস্যা হচ্ছে? দেখো 7-এর গুণিতকগুলো ছিল 14, 21, 28, 35। 7-এর সঙ্গে যেসব সংখ্যা গুণ করে ওই গুণিতকগুলো পাওয়া যায় সেগুলো সবই 7-এর চেয়ে ছোট সংখ্যা এবং তাদের গুণিতকগুলো আমরা ইতিমধ্যেই বাদ দিয়ে দিয়েছি।
আরো পরিষ্কারভাবে বোঝার জন্য উইকিপিডিয়ার এই অ্যানেমেশনটি দেখতে পারো (এখানে 2 থেকে 120 পর্যন্ত সংখ্যাগুলোর মধ্যে মৌলিক সংখ্যাগুলো বের করা হয়েছে):
এবারে ইমপ্লিমেন্ট করার পালা। আমরা তালিকা রাখার জন্য একটি অ্যারে ব্যবহার করব। ধরা যাক, তার নাম হচ্ছে ara। অ্যারেটি এমনভাবে তৈরি করতে হবে, যাতে কোনো একটি সংখ্যা n-এর অবস্থা (অর্থাৎ সেটি মৌলিক কি না) ara[n] দিয়ে প্রকাশ করা যায়। যদি ara[n]-এর মান 1 হয়, তবে n মৌলিক সংখ্যা আর ara[n]-এর মান 0 হলে n মৌলিক সংখ্যা নয়। ইমপ্লিমেন্টেশনের আগে অ্যালগরিদমটা লেখা যাক:
ধাপ ১: ধরা যাক, অ্যারেতে nটি উপাদান আছে। শুরুতে অ্যারের সব উপাদানের মান 1 বসাই।
ধাপ ২: অ্যারের প্রতিটি উপাদানের জন্য সেটির মান 1 কি না তা পরীক্ষা করি। যদি 1, হয় তবে তৃতীয় ধাপে যাই।
ধাপ ৩: ওই সংখ্যাকে 2 থেকে m পর্যন্ত ক্রমিক সংখ্যাগুলো দিয়ে গুণ করি এবং গুণফল যত হবে, অ্যারের তত নম্বর উপাদানে শূন্য (0) বসাই। অর্থাৎ সেটি যে মৌলিক নয় তা চিহ্নিত করি। এখানে m-এর মান এমন হবে যেন ঐ সংখ্যার সঙ্গে m-এর গুণফল n-এর চেয়ে ছোট বা সমান হয়।
এখন তোমরা কোডটি লিখার চেষ্টা করো। কমপক্ষে তিন ঘণ্টা নিজে চেষ্টা করার পর এবারে আমার কোড দেখো।
প্রাইম নম্বর বের করতে পেরে তোমরা নিশ্চয়ই বেশ খুশি? কিন্তু আমাদের চেষ্টা এখানেই থেমে থাকবে না। আমরা এখন দেখব আরেকটি চমৎকার পদ্ধতি, গ্রিক গণিতবিদ ইরাতোসথেনেস (Eratosthenes) আজ থেকে দুই হাজার বছরেরও আগে এই পদ্ধতি আবিষ্কার করেছিলেন। এজন্য-এর নাম হচ্ছে সিভ অব ইরাতোসথেনেস (Sieve of Eratosthenes)।
পদ্ধতিটি ব্যাখ্যা করা যাক। ধরো, আমরা 2 থেকে 40 পর্যন্ত সব মৌলিক সংখ্যা বের করব। শুরুতে সব সংখ্যা লিখে ফেলি: 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 , 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40. এখন দেখো, তালিকার প্রথম সংখ্যা হচ্ছে 2। এবারে 2-এর সব গুণিতক (2 বাদে, মানে 2-এর চেয়ে বড়গুলো আরকী) বাদ দিয়ে দাও। তাহলে থাকবে: 2, 3, 5, 7, 9, 11, 13, 15, 17, 19 , 21, 23, 25, 27, 29, 31, 33, 35, 37, 39. এখন তালিকার দ্বিতীয় সংখ্যা 3-এর সব গুণিতক (3-এর চেয়ে বড়গুলো) বাদ দাও। 2, 3, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37. এখন তালিকার তৃতীয় সংখ্যা 5-এর সব গুণিতক (5 বাদে) বাদ দাও। 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37. পরবর্তী সংখ্যা হচ্ছে 7 কিন্তু সেটির গুণিতক খোঁজার চেষ্টা করা বৃথা। কারণ তালিকার সর্বোচ্চ সংখ্যা 37-এর বর্গমূল 7-এর চেয়ে ছোট। সুতরাং 7-এর যে গুণিতকগুলো তালিকায় ছিল সেগুলো ইতিমধ্যে তালিকা থেকে বাদ পড়েছে। কারণটি বুঝতে সমস্যা হচ্ছে? দেখো 7-এর গুণিতকগুলো ছিল 14, 21, 28, 35। 7-এর সঙ্গে যেসব সংখ্যা গুণ করে ওই গুণিতকগুলো পাওয়া যায় সেগুলো সবই 7-এর চেয়ে ছোট সংখ্যা এবং তাদের গুণিতকগুলো আমরা ইতিমধ্যেই বাদ দিয়ে দিয়েছি।
আরো পরিষ্কারভাবে বোঝার জন্য উইকিপিডিয়ার এই অ্যানেমেশনটি দেখতে পারো (এখানে 2 থেকে 120 পর্যন্ত সংখ্যাগুলোর মধ্যে মৌলিক সংখ্যাগুলো বের করা হয়েছে):
এবারে ইমপ্লিমেন্ট করার পালা। আমরা তালিকা রাখার জন্য একটি অ্যারে ব্যবহার করব। ধরা যাক, তার নাম হচ্ছে ara। অ্যারেটি এমনভাবে তৈরি করতে হবে, যাতে কোনো একটি সংখ্যা n-এর অবস্থা (অর্থাৎ সেটি মৌলিক কি না) ara[n] দিয়ে প্রকাশ করা যায়। যদি ara[n]-এর মান 1 হয়, তবে n মৌলিক সংখ্যা আর ara[n]-এর মান 0 হলে n মৌলিক সংখ্যা নয়। ইমপ্লিমেন্টেশনের আগে অ্যালগরিদমটা লেখা যাক:
ধাপ ১: ধরা যাক, অ্যারেতে nটি উপাদান আছে। শুরুতে অ্যারের সব উপাদানের মান 1 বসাই।
ধাপ ২: অ্যারের প্রতিটি উপাদানের জন্য সেটির মান 1 কি না তা পরীক্ষা করি। যদি 1, হয় তবে তৃতীয় ধাপে যাই।
ধাপ ৩: ওই সংখ্যাকে 2 থেকে m পর্যন্ত ক্রমিক সংখ্যাগুলো দিয়ে গুণ করি এবং গুণফল যত হবে, অ্যারের তত নম্বর উপাদানে শূন্য (0) বসাই। অর্থাৎ সেটি যে মৌলিক নয় তা চিহ্নিত করি। এখানে m-এর মান এমন হবে যেন ঐ সংখ্যার সঙ্গে m-এর গুণফল n-এর চেয়ে ছোট বা সমান হয়।
এখন তোমরা কোডটি লিখার চেষ্টা করো। কমপক্ষে তিন ঘণ্টা নিজে চেষ্টা করার পর এবারে আমার কোড দেখো।
#include <stdio.h>
#include <math.h>
const int size = 40;
int ara[size];
void print_ara()
{
int i;
for(i = 2; i < size; i++) {
printf("%4d", ara[i]);
}
printf("\n");
for(i = 2; i < size; i++) {
printf("----");
}
printf("\n");
for(i = 2; i < size; i++) {
printf("%4d", i);
}
printf("\n\n\n");
}
void sieve()
{
int i, j, root;
for(i = 2; i < size; i++) {
ara[i] = 1;
}
root = sqrt(size);
print_ara();
for(i = 2; i <= root; i++) {
if(ara[i] == 1) {
for(j = 2; i * j <= size; j++) {
ara[i * j] = 0;
}
print_ara();
}
}
}
int is_prime(int n)
{
int i;
if(n < 2) {
return 0;
}
return ara[n];
}
int main()
{
int n, m;
sieve();
while(1) {
printf("Please enter a number (enter 0 to exit): ");
scanf("%d", &n);
if(n == 0) {
break;
}
if(n >= size) {
printf("The number should be less than %d\n", size);
continue;
}
if(1 == is_prime(n)) {
printf("%d is a prime number.\n", n);
}
else {
printf("%d is not a prime number.\n", n);
}
}
return 0;
}
প্রোগ্রাম: ১০.২
প্রতিবার অ্যারের অবস্থা
বোঝানোর জন্য আমি একটি ফাংশন ব্যবহার করেছি, print_ara()। তোমরা দেখো এবারে
ইনপুট নেওয়ার আগেই আমরা sieve() ফাংশন কল করে অ্যারেটি তৈরি করে ফেলেছি।
তারপর যতবারই ইনপুট নাও, কোনো চিন্তা নেই, ইনপুট যদি n হয় তবে ara[n]-এর
মান পরীক্ষা করলেই চলে, মান যদি 1 হয় তবে n মৌলিক সংখ্যা, যদি 0 হয় তবে n
মৌলিক সংখ্যা নয়। কত পর্যন্ত সংখ্যা হিসাব করতে চাও সেটি size-এ বসিয়ে
দিলেই হবে। এখন এই প্রোগ্রামে গতি নিয়ে কোনো সমস্যা নেই। খুবই ফাস্ট
(fast)। কিন্তু আর কোনো সমস্যা তোমাদের চোখে পড়ছে? তোমরা কি বুঝতে পারছ যে
প্রোগ্রামটি অনেক বেশি মেমোরি খরচ করে? ধরো, আমরা যদি 100 কোটি পর্যন্ত
সংখ্যা মৌলিক কি না সেটি বের করতে চাই, তাহলে তো আমাদের 100 কোটির একটি
অ্যারে দরকার হবে। 'সময় বাঁচাব না মেমোরি' সমস্যায় প্রোগ্রামারদের প্রায়ই
পড়তে হয়। আমাদের সমস্যার ক্ষেত্রে আমরা একটি মাঝামাঝি সমাধানে পৌঁছতে পারি।
n-এর সর্বোচ্চ মান যত হবে তার বর্গমূলটিকে size-এর মান হিসেবে নিতে পারি।
তোমাকে যদি বলা হয়, n-এর মান সর্বোচ্চ 100000000 (দশ কোটি) পর্যন্ত হতে
পারে তাহলে তুমি এর বর্গমূল অর্থাৎ 10000 পর্যন্ত সংখ্যাগুলোর জন্য sieve
ফাংশন ব্যবহার করে মৌলিক সংখ্যাগুলো বের করবে। তারপর কী করবে? নাহ্, আর
কিছু বলা যাবে না, তোমরাই চিন্তা করে ঠিক করো কী করবে। আরেকটি কথা বলে
দেওয়া দরকার। একটি ইন্টিজার কিন্তু চার বাইট জায়গা দখল করে, যেখানে একটি
ক্যারেক্টার করে এক বাইট। সুতরাং ইন্টিজারের পরিবর্তে তোমরা ক্যারেক্টারের
অ্যারে ব্যবহার করে মেমোরি খরচ চার ভাগের এক ভাগে নামিয়ে আনতে পারো। আমাদের
তো আসলে ইন্টিজার অ্যারের দরকার নেই, কারণ অ্যারেতে কেবল দুই ধরনের মান
থাকবে 0 বা 1। এটি ছাড়াও আমার লেখা sieve ফাংশনে আরও বেশ কিছু উপায় আছে
ইফিসিয়েন্সি বাড়ানোর। এর মধ্যে একটি হচ্ছে গুণের বদলে যোগ করা। তোমরা সেটি
করার চেষ্টা করো।
আবারও অ্যারে-
গণিত
শিক্ষকের জন্য লেখা প্রোগ্রামটির কথা মনে আছে তো? সেই যে আমরা তিনটি
অ্যারে ব্যবহার করে প্রোগ্রাম লিখেছিলাম ছাত্র-ছাত্রীদের গণিত পরীক্ষার মোট
নম্বর বের করার জন্য। মনে না থাকলে প্রোগ্রামটি আবার দেখে নাও।
আমরা প্রথম সাময়িক পরীক্ষার নম্বর
রেখেছিলাম একটি অ্যারেতে, দ্বিতীয় সাময়িক পরীক্ষার নম্বর আরেকটি অ্যারেতে,
ফাইনাল পরীক্ষার নম্বর আরেকটি অ্যারেতে আর মোট নম্বর আরও একটি অ্যারেতে –
মোট চারটি অ্যারে ব্যবহার করেছি। তো এখন যদি পুরো স্কুলের ফলাফল নির্ণয়ের
জন্য প্রোগ্রাম লেখতে হয়, তবে সব ক্লাসের সব ছাত্র-ছাত্রীর সব বিষয়ের সব
পরীক্ষার জন্য একটি করে অ্যারে ব্যবহার করা খুবই ঝামেলার কাজ হয়ে যাবে। তাই
মোটামুটি সব প্রোগ্রামিং ল্যাঙ্গুয়েজেই মাল্টি-ডাইমেনশনাল অ্যারের
ব্যবস্থা আছে। এতক্ষণ আমরা যেসব অ্যারে ব্যবহার করেছি, তার সবগুলোই ছিল এক
ডাইমেনশনের অ্যারে।
এখন ওই প্রোগ্রামটি আমরা একটু
অন্যভাবে লিখব, একটি মাত্র অ্যারে ব্যবহার করে। আপাতত কষ্ট কমানোর জন্য ধরি
ক্লাসে মোট দশজন ছাত্র-ছাত্রী আছে। নিচের ছকে তাদের নম্বরগুলো লিখলাম:
Roll: 1
|
Roll: 2
|
Roll: 3
|
Roll: 4
|
Roll: 5
|
Roll: 6
|
Roll: 7
|
Roll: 8
|
Roll: 9
|
Roll: 10
| |
First terminal exam
|
80
|
70
|
92
|
78
|
58
|
83
|
85
|
66
|
99
|
81
|
Second terminal exam
|
75
|
67
|
55
|
100
|
91
|
84
|
79
|
61
|
90
|
97
|
Final exam
|
98
|
67
|
75
|
89
|
81
|
83
|
80
|
90
|
88
|
77
|
Total marks
|
Total Marks সারির ঘরগুলো ফাঁকা, কারণ
এগুলো এখনো হিসাব করিনি। প্রথম সাময়িক পরীক্ষার 25%, দ্বিতীয় সাময়িক
পরীক্ষার 25% এবং ফাইনাল পরীক্ষার 50% নম্বর যোগ করে মোট নম্বর বের করতে
হবে। এখন দেখো, আমাদের ছকে তাদের নম্বরগুলো রাখতে হয়েছে 4 টা সারি (row)
এবং 10 টা কলামে। আমরা আগে যেই প্রোগ্রাম লিখেছিলাম, তাতে প্রথম রো-এর জন্য
একটি অ্যারে, দ্বিতীয় রো-এর জন্য একটি, তৃতীয় রো-এর জন্য একটি এবং চতুর্থ
রো-এর জন্য একটি অ্যারে ব্যবহার করেছিলাম। এখন ব্যবহার করব একটি 2-D অ্যারে
(টু ডাইমেনশনাল অ্যারে)।
2-D অ্যারে ডিক্লেয়ার করার নিয়ম হচ্ছে: data_type array_name [number of rows][number of columns];
যেমন ওপরের ছকটা যদি marks নামের একটি 2-D অ্যারেতে রাখতে চাই, তবে লিখতে হবে: int marks[4][10];
এখন, অ্যারের প্রথম রো হচ্ছে
marks[0], দ্বিতীয় রো হচ্ছে marks[1], তৃতীয় রো হচ্ছে marks[2] এবং চতুর্থ
রো হচ্ছে marks[3]। আবার marks[0][0] হচ্ছে প্রথম রো-এর প্রথম কলাম,
marks[0][1] হচ্ছে প্রথম রো-এর দ্বিতীয় কলাম, marks[0][5] হচ্ছে প্রথম
রো-এর ষষ্ঠ কলাম, marks[1][0] হচ্ছে দ্বিতীয় রো-এর প্রথম কলাম, marks[2][3]
হচ্ছে তৃতীয় রো-এর চতুর্থ কলাম। আশা করি, বুঝতে পেরেছ।
এখন বলো তো, যার রোল নম্বর 10 তার দ্বিতীয় সাময়িক পরীক্ষার নম্বর কোন ঘরে আছে? আর marks[0][0] ঘরে কার এবং কোন পরীক্ষার নম্বর আছে?
marks[0][0]-এ থাকবে রোল 1-এর
প্রথম সাময়িক পরীক্ষার নম্বর আর marks[1][9]-এ থাকবে রোল 10-এর দ্বিতীয়
সাময়িক পরীক্ষার নম্বর। অ্যারেতে সংখ্যাগুলো এভাবে রাখতে পারি:
int marks[4][10] = {{80, 70, 92, 78, 58, 83, 85, 66, 99, 81}, {75, 67, 55, 100, 91, 84, 79, 61, 90, 97}, {98, 67, 75, 89, 81, 83, 80, 90, 88, 77}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
এখানে খেয়াল করেছ যে আমরা আসলে একটি
অ্যারের ভেতর চারটি এক ডাইমেনশনের অ্যারে রেখেছি। marks[0]তে আছে প্রথম
সাময়িক পরীক্ষায় সবার নম্বর, marks[1]-এ দ্বিতীয় সাময়িক পরীক্ষার নম্বর,
marks[2]-এ ফাইনাল পরীক্ষার নম্বর এবং marks[3]তে মোট নম্বর (যেহেতু এখনো
এটি হিসাব করিনি, তাই সব 0 লিখে দিলাম)।
এখন সম্পূর্ণ প্রোগ্রামটি তোমার কম্পিউটারে টাইপ করে কম্পাইল ও রান করো।
#include <stdio.h>
int main()
{
int marks[4][10] = {{80, 70, 92, 78, 58, 83, 85, 66, 99, 81}, {75, 67, 55, 100, 91, 84, 79, 61, 90, 97}, {98, 67, 75, 89, 81, 83, 80, 90, 88, 77}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
int col;
for(col = 0; col < 10; col++) {
marks[3][col] = marks[0][col] / 4.0 + marks[1][col] / 4.0 + marks[2][col] / 2.0;
printf("Roll NO: %d Total Marks: %d\n", col + 1, marks[3][col]);
}
return 0;
}
প্রোগ্রাম: ১১.১
নম্বরগুলো আগে না লিখে যদি আমরা ব্যবহারকারীর কাছ থেকে ইনপুট হিসেবে নিতে চাইতাম তাহলে কী করতে হতো?
int marks[4][10];
int i, j;
for (i = 0; i < 4; i++) {
for (j = 0; j < 10; j++) {
scanf("%d", &ara[i][j]);
}
}
এভাবে নেস্টেড লুপের সাহায্যে আমরা
ইনপুট নিতে পারি। প্রথম লুপটি ব্যবহার করা হয়েছে রো-এর জন্য আর দ্বিতীয়
লুপটি কলামের জন্য। যখন i = 0, অর্থাৎ প্রথম রো-এর জন্য আমরা j = 0 থেকে 9
পর্যন্ত সব ঘরের ইনপুট নিচ্ছি, তারপর আবার i = 1 (দ্বিতীয় রো)-এর জন্য j = 0
থেকে 9 (প্রতি কলাম) পর্যন্ত সব ঘরের মান ইনপুট নেওয়া হচ্ছে।
এখন আমরা 1 থেকে 10 পর্যন্ত সংখ্যাগুলোর
নামতা বের করার জন্য 2-D অ্যারে ব্যবহার করে একটি প্রোগ্রাম লিখব। একের
নামতা হবে প্রথম রো-তে, দুইয়ের নামতা দ্বিতীয় রো-তে … দশের নামতা দশম রো-তে
থাকবে। তোমরা কি প্রোগ্রামটি নিজে নিজে লিখার চেষ্টা করবে? এক ঘণ্টার
মধ্যেও যদি না হয়, তবে আমার কোডটি দেখো। প্রোগ্রামিং শেখার সময় অনেক সহজ
প্রোগ্রাম লিখতেও প্রচুর সময় লাগে, তাতে হতাশ হবার কিছু নেই।
#include <stdio.h>
int main()
{
int namta[10][10];
int row, col;
for (row = 0; row < 10; row++) {
for(col = 0; col < 10; col++) {
namta[row][col] = (row + 1) * (col + 1);
}
}
for (row = 0; row < 10; row++) {
for(col = 0; col < 10; col++) {
printf("%d x %d = %d\n", (row + 1), (col + 1), namta[row][col]);
}
printf("\n");
}
return 0;
}
প্রোগ্রাম: ১১.২
সম্পূর্ণ আউটপুট স্ক্রিনে না-ও আটতে পারে, তাতে চিন্তার কিছু নেই।
এখন তোমাদের জন্য একটি সমস্যা।
ওপরের প্রোগ্রামটি পরিবর্তন করো যাতে আউটপুট হিসেবে আমরা দেখতে পারি যে
namta অ্যারেতে মোট কয়টি জোড় ও কয়টি বেজোড় সংখ্যা আছে। সেই সঙ্গে অ্যারেতে
যতগুলো সংখ্যা আছে, তার যোগফলও বের করতে হবে। আশা করি, প্রোগ্রামটি ঠিকঠাক
লিখতে পেরেছ। যদি কোনো সমস্যা হয়, তবে তুমি তোমার বন্ধুর সাহায্য নিতে
পারো।
আচ্ছা, কেউ যদি বলে, সার্কভুক্ত
সাতটি দেশের নাম একটি অ্যারেতে রাখতে, তাহলে কী করবে? একটি char type
অ্যারেতে একটি দেশের নাম রাখা যায়। যেমন: char country[] = "Bangladesh";।
তাহলে সাতটি দেশের নাম রাখার জন্য আমাদের একটি 2-D অ্যারে ব্যবহার করতে
হবে। এই অ্যারেতে মোট কয়টি রো থাকবে? সাতটি। কলাম কয়টি থাকবে? জানি না।
আসলে একেক দেশের নামের দৈর্ঘ্য তো একেক রকম। তাই আমরা একটি কাজ করতে পারি,
কলামে 100টি ক্যারেক্টার রাখার ব্যবস্থা করতে পারি, কারণ সার্কের কোনো
দেশের নামে তো 100টির বেশি ক্যারেক্টার নেই, কম আছে।
#include <stdio.h>
int main()
{
char saarc[7][100] = {"Bangladesh", "India", "Pakistan", "Sri Lanka", "Nepal", "Bhutan", "Maldives"};
int row;
for (row = 0; row < 7; row++) {
printf("%s\n", saarc[row]);
}
return 0;
}
প্রোগ্রাম: ১১.৩
প্রোগ্রামটি তোমার কম্পিউটারে লিখে রান
করাও। এখন তোমরা বলো তো, saarc[3][3], saarc[0][5] ও saarc[5][0] – এই
তিনটি ঘরে কী কী ক্যারেক্টার আছে? একটু পরে একটি প্রোগ্রাম লিখব, তার সঙ্গে
তোমার উত্তর মিলিয়ে নেবে।
আমরা যদি ওই অ্যারের প্রতিটি ক্যারেক্টার আলাদাভাবে প্রিন্ট করতে চাই, তবে প্রোগ্রামটি এভাবে লিখতে পারি:
#include <stdio.h>
#include <string.h>
int main()
{
char saarc[7][100] = {"Bangladesh", "India", "Pakistan", "Sri Lanka", "Nepal", "Bhutan", "Maldives"};
int row, col, name_length;
for (row = 0; row < 7; row++) {
name_length = strlen(saarc[row]);
for(col = 0; col < name_length; col++) {
printf("%c ", saarc[row][col]);
}
printf("\n");
}
return 0;
}
প্রোগ্রাম: ১১.৪
আবার যদি দেখতে চাই কোন ঘরে কী আছে, তাহলে রো এবং কলাম নম্বরসহ প্রিন্ট করতে পারি।
#include <stdio.h>
#include <string.h>
int main()
{
char saarc[7][100] = {"Bangladesh", "India", "Pakistan", "Sri Lanka", "Nepal", "Bhutan", "Maldives"};
int row, col, name_length;
for (row = 0; row < 7; row++) {
name_length = strlen(saarc[row]);
for(col = 0; col < name_length; col++) {
printf("(%d, %d) = %c, ", row, col, saarc[row][col]);
}
printf("\n");
}
return 0;
}
প্রোগ্রাম: ১১.৫
এবারে নিচের ছকটি দেখো।
6
|
4
|
7
|
8
|
9
|
3
|
7
|
1
|
9
|
9
|
8
|
6
|
4
|
2
|
7
|
2
|
4
|
2
|
5
|
9
|
4
|
1
|
6
|
7
|
3
|
এখন 2-D অ্যারে ব্যবহার করে তোমাদের
দুটি প্রোগ্রাম লিখতে হবে। প্রথম প্রোগ্রামটির কাজ হবে প্রতিটি রো-এর
সংখ্যাগুলোর যোগফল বের করা আর দ্বিতীয় প্রোগ্রামের কাজ হবে প্রতিটি কলামের
সংখ্যাগুলোর যোগফল বের করা।
প্রথম প্রোগ্রামের আউটপুট হবে এই রকম:
Sum of row 1: 34
Sum of row 2: 29
Sum of row 3: 27
Sum of row 4: 22
Sum of row 5: 21
দ্বিতীয় প্রোগ্রামের আউটপুট হবে এই রকম:
Sum of column 1: 23
Sum of column 2: 22
Sum of column 3: 20
Sum of column 4: 31
Sum of column 5: 37
তোমাদের অনেকেরই দ্বিতীয়
প্রোগ্রামটি লিখতে একটু সময় লাগতে পারে, কিন্তু হতাশার কোন কারণ নেই। সময়
লাগাই স্বাভাবিক। এখন নিচের ছকটি দেখো। আগের ছকটির সঙ্গে-এর কোথায় যেন একটু
মিল রয়েছে!
6
|
3
|
8
|
2
|
4
|
4
|
7
|
6
|
4
|
1
|
7
|
1
|
4
|
2
|
6
|
8
|
9
|
2
|
5
|
7
|
9
|
9
|
7
|
9
|
3
|
আসলে আগের ছকের রোগুলো নতুন ছকের কলাম,
আর আগের ছকের কলামগুলো নতুন ছকের রো। যেমন আগের ছকের প্রথম রো-টি ছিল: 6,
4, 7, 8, 9। আর এই ছকের প্রথম কলাম হচ্ছে: 6, 4, 7, 8, 9। একইভাবে আগের
ছকের দ্বিতীয় রো নতুন ছকের দ্বিতীয় কলামের সঙ্গে মেলে। এখন আমরা একটি
প্রোগ্রাম লিখব, যেটি একটি 5 x 5 অ্যারেকে (অর্থাৎ 5 রো এবং 5 কলামবিশিষ্ট
অ্যারে), আরেকটি 5 x 5 অ্যারেতে এমনভাবে কপি করবে, যাতে প্রথম অ্যারের
রোগুলো হয় দ্বিতীয় অ্যারের কলাম আর প্রথম অ্যারের কলামগুলো হয় দ্বিতীয়
অ্যারের রো। মানে ওপরের ছক দুটির মত আরকি। যেমন প্রথম অ্যারের প্রথম রো যদি
হয়: 1, 2, 3, 4, 5 তাহলে দ্বিতীয় অ্যারের প্রথম কলাম হবে 1, 2, 3, 4, 5।
তোমার কি বিষয়টি একটু জটিল মনে হচ্ছে? তাহলে কিছুক্ষণ বিশ্রাম নিয়ে তারপর
নিচের প্রোগ্রামটি কম্পাইল করো, রান করো, আউটপুট দেখো এবং কীভাবে কাজ করছে
বোঝার চেষ্টা করো।
#include <stdio.h>
#include <string.h>
int main()
{
int ara1[5][5] = {{1, 2, 3, 4, 5}, {10, 20, 30, 40, 50}, {100, 200, 300, 400, 500}, {1000, 2000, 3000, 4000, 5000}, {10000, 20000, 30000, 40000, 50000}};
int ara2[5][5];
int r, c;
printf("Content of first array (ara1): \n");
for (r = 0; r < 5; r++) {
for(c = 0; c < 5; c++) {
printf("%d ", ara1[r][c]);
}
printf("\n");
}
printf("\n");
// now start copy
for (r = 0; r < 5; r++) {
for(c = 0; c < 5; c++) {
ara2[c][r] = ara1[r][c];
}
}
printf("Content of second array (ara2): \n");
for (r = 0; r < 5; r++) {
for(c = 0; c < 5; c++) {
printf("%d ", ara2[r][c]);
}
printf("\n");
}
return 0;
}
প্রোগ্রাম: ১১.৬
তোমরা যদি এতক্ষণ 2-D অ্যারের সব উদাহরণ
এবং যেসব প্রোগ্রাম নিজে লিখতে বলেছি, সেগুলো সব কম্পিউটারে রান করে থাকো
এবং বুঝে থাকো (বুঝতে হলে অবশ্যই চিন্তা করতে হবে) তবে তোমাদের আর 2-D
অ্যারে নিয়ে সমস্যা হওয়ার কথা নয়। অ্যারে কিন্তু 3-D, 4-D কিংবা আরও বেশি
ডাইমেনশনের হতে পারে, তবে সেগুলো নিয়ে এই বইতে আর আলোচনা করব না।
বাইনারি সংখ্যা-
আমরা তো দৈনন্দিন জীবনে নানা হিসাব-নিকাশের জন্য দশভিত্তিক (decimal) সংখ্যা পদ্ধতি ব্যবহার করি। কিন্তু কম্পিউটার ব্যবহার করে দুইভিত্তিক বা বাইনারি (binary) সংখ্যা পদ্ধতি। দশভিত্তিক সংখ্যা পদ্ধতিতে আছে মোট দশটি অঙ্ক 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 আর বাইনারিতে দুটি, 0 আর 1।
আমরা এই অধ্যায়ে বাইনারি সংখ্যা পদ্ধতির কিছু মৌলিক জিনিস দেখব আর বাইনারি
থেকে ডেসিমাল এবং ডেসিমাল থেকে বাইনারি সংখ্যায় রূপান্তর করা শিখব।
ডেসিমালে আমরা গণনা করি এভাবে: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, … 19, 20, 21, …, 98, 99, 100, 101 ...। দেখো, যখনই আমরা ডান দিকের ঘরে (এককের ঘরে) দশটি অঙ্ক ব্যবহার করে ফেলি, তখন তার বাঁয়ে দশকের ঘরের অঙ্কের মান এক বাড়াই (আর যদি না থাকে তাহলে 1 বসাই বা 0-এর সঙ্গে 1 যোগ করি আর কি, কারণ 9 আর 09 কিন্তু একই কথা, তাই 09-এর পরবর্তি সংখ্যা হচ্ছে 10), আবার দশকের ঘরে 0 থেকে 9 সব অঙ্ক ব্যবহার করে ফেলার পরে শতকের ঘরের অঙ্কের মান এক বাড়াই (আর যদি না থাকে তাহলে 1 বসাই বা 0-এর সঙ্গে 1 যোগ করি আর কি)। তেমনই বাইনারিতে আমরা গণনা করব এইভাবে: 0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011 ...। যেহেতু অঙ্ক মাত্র দুটি, তাই দুটি অঙ্কের ব্যবহার হয়ে গেলেই বাঁ দিকের ঘরে এক বসাতে হয় বা 0-এর সঙ্গে 1 যোগ করতে হয় (বাঁ দিকে তো আমরা ইচ্ছামত শূন্য বসাতে পারি)।
বাইনারি সিস্টেমে অবশ্য আমরা এককের ঘর, দশকের ঘর, শতকের ঘর, সহস্রের ঘর না বলে বলব একের ঘর, দুইয়ের ঘর, চারের ঘর, আটের ঘর। কেন বল তো? একটু চিন্তা করো।
ডেসিমালে যেমন 10 লিখতে দুটি অঙ্ক লাগে, 100 লিখতে তিনটি, 1000 লিখতে চারটি, তেমনই বাইনারিতে দুই লিখতে দুটি (10), চার লিখতে তিনটি (100), আট লিখতে চারটি (1000), ষোল লিখতে পাঁচটি (10000) অঙ্ক ব্যবহার করতে হয়। ডেসিমালে ডান দিকের প্রথম অঙ্ক (100 = 1) হচ্ছে এককের ঘর, দ্বিতীয় অঙ্ক (101 = 10) হচ্ছে দশকের ঘর, তৃতীয় অঙ্ক (102 = 100) হচ্ছে শতকের ঘর, তেমনই বাইনারিতে ডানদিকের প্রথম অঙ্ক (20 = 1) হচ্ছে একের ঘর, পরের অঙ্ক (21 = 2) হচ্ছে দুইয়ের ঘর, তারপর (22 = 4) হচ্ছে চারের ঘর, এই রকম।
দশভিত্তিক সংখ্যায় যেমন যোগ, বিয়োগ, গুণ, ভাগ করা যায়, তেমনই বাইনারিতে করা যায়। আসলে যোগ করতে পারলে কিন্তু বাকি কাজ করা কোনো ব্যাপার নয়। আবার বাইনারিতে ভগ্নাংশের ব্যাপার আছে, তবে আমি কেবল পূর্ণসংখ্যা নিয়েই আলোচনা করব।
যোগের ক্ষেত্রে মূল হিসাবগুলো হচ্ছে:
0 + 0 = 0,
0 + 1 = 1,
1 + 0 = 1,
1 + 1 = 10।
ডেসিমালের মতোই হিসাব, 1 + 1 এর ক্ষেত্রে দেখো, দুইয়ের (10) শূন্য এল প্রথমে, হাতে থাকে এক, সেটি পরে লিখলাম।
101 + 101 = কত?
প্রথমে একের ঘরের যোগ, 1 + 1 = 10। তাই যোগফলের একের ঘরে বসবে 0 আর হাতে থাকল 1 (carry)। এবারে দুইয়ের ঘরে, 0 + 0 = 0, এখন এই 0-এর সঙ্গে হাতের 1 যোগ করতে হবে। তাহলে যোগফলের দুইয়ের ঘরে বসবে 1। এবারে চারের ঘরের যোগ করলে পাই, 1 + 1 = 10। হাতে কিছু নেই (কোনো carry নেই)। সুতরাং চারের ঘরে বসবে 0 আর 1 বসবে আটের ঘরে। যোগফল: 1010। এবারে বলো 1011 + 1011 = কত? যোগ করে যদি দেখো যোগফল 10110 হয়নি, তাহলে তুমি যোগে কোথাও ভুল করেছ।
বিয়োগের ক্ষেত্রেও ডেসিমালের মতো হিসাব হবে।
0 – 0 = 0,
1 – 0 = 1,
1 – 1 = 0,
0 – 1 = 1।
শেষেরটি খেয়াল করো, 23 – 15 করার সময় আমরা কী করি? তখন 3-এর বাঁয়ে একটি কাল্পনিক 1 ধরে নিই (বা 1 ধার করি), তারপর 13 – 5 = 8 লেখি। আর যেই একটি ধার করলাম, সেটি পরের ঘরে 1-এর সঙ্গে যোগ করে দিই। তেমনই বাইনারিতে 0 – 1 করতে গেলে 0-এর বাঁয়ে একটি এক ধরব, তখন সংখ্যাটি হবে 10 (দুই), এই দুই থেকে এক বাদ দিলে এক থাকবে। পরের ঘরে একটি এক যোগ করতে হবে (যেই সংখ্যাটি বিয়োগ হচ্ছে তার সঙ্গে)।
110 – 101 = কত?
একের ঘরে 0 থেকে 1 বাদ দিলে থাকে 1, এখানে 1 ধার করতে হয়েছে। তাই 101-এর দুইয়ের ঘরে সেটি যোগ করে দেব। তাহলে দুইয়ের ঘরে 1 – 1 = 0, চারের ঘরে 1 – 1 = 0। তাই বিয়োগফল হবে: 001 বা 1। যোগ-বিয়োগ পারলে গুণ-ভাগ না পারার কারণ নেই। ডেসিমালের মতোই নিয়ম।
আবার কোনো ডেসিমাল সংখ্যাকে আমরা নির্দিষ্ট অঙ্ক x 10ওই অঙ্কের অবস্থান-এর যোগফল হিসেবে যেমন লিখতে পারি, বাইনারি সংখ্যাকেও নির্দিষ্ট অঙ্ক x 2ওই অঙ্কের অবস্থান-এর যোগফল হিসেবে লেখা যায়। যেমন: 1903 = 1 x 103 + 9 x 102 + 0 x 101 + 3 x 100।
বাইনারি: 10110 = 1 x 24 + 0 x 23 + 1 x 22 + 1 x 21 + 0 x 20। ইংরেজিতে একে বলে Exponential Expression।
এখন কোনো বাইনারি সংখ্যার মান যদি ডেসিমালে বের করতে চাই, তবে
প্রথমে বাইনারি সংখ্যাটিকে এক্সপোনেনশিয়াল এক্সপ্রেশন আকারে লিখতে হবে।
তারপর গুণফলগুলো ডেসিমালে হিসাব করতে হবে। নিচের উদাহরণটি দেখো:
10110 = 1 x 24 + 0 x 23 + 1 x 22 + 1 x 21 + 0 x 20 = 1 x 16 + 0 x 8 + 1 x 4 + 1 x 2 + 0 x 1
= 16 + 0 + 4 + 2 + 0 = 22
অর্থাৎ বাইনারি 10110 = ডেসিমাল 22। আমরা অনেকক্ষণ কোনো প্রোগ্রামিং করছি না, চলো বাইনারি সংখ্যার ডেসিমাল মান বের করার একটি প্রোগ্রাম লিখে ফেলি। পদ্ধতি তো জানা হয়ে গেছে। এখন গুরুত্বপূর্ণ প্রশ্ন হচ্ছে, বাইনারি সংখ্যা রিড করব কী দিয়ে? আমরা স্ট্রিং ব্যবহার করতে পারি।
char binary[] = "10110";
int len = 5; // স্ট্রিংয়ের দৈর্ঘ্য 5।
int decimal = 0; // এখনো কোনো হিসাব করিনি, তাই ধরলাম ডেসিমাল মান 0।
এবারে আমরা একটি লুপের সাহায্যে বাইনারি সংখ্যার প্রতিটি অঙ্কের সঙ্গে 2ওই অঙ্কের অবস্থান গুণ করে সেটি ডেসিমালের সঙ্গে যোগ করে দেব। প্রথম ক্যারেক্টার অর্থাৎ binary[0]তে তো '1' আছে,-এর অবস্থান কত বলো তো?-এর অবস্থান হচ্ছে 4। তারপরের অঙ্কের বেলায় অবস্থানের মান এক কমবে, এভাবে একেবারে শেষের অঙ্কের বেলায় অবস্থান হবে 0।
int position = 4;
int indx;
for(indx = 0; indx < len; indx++) {
decimal = decimal + pow(2, position);
position--;
}
লুপ থেকে বের হলে আমরা সম্পূর্ণ বাইনারি সংখ্যার ডেসিমাল মান পেয়ে যাব। এখানে দেখো, আমি pow ফাংশন ব্যবহার করেছি। এটির কাজ বলা আছে math.h হেডার ফাইলে। ab-এর মান বের করার জন্য pow(a, b) বলে দিলেই হয়। তাহলে আমাদের পুরো প্রোগ্রামটি দাঁড়াচ্ছে এই রকম:
#include <stdio.h>
#include <string.h>
#include <math.h>
int main()
{
char binary[65];
int len, decimal, power, i;
printf("Enter the binary number: ");
scanf("%s", binary);
decimal = 0;
len = strlen(binary);
power = len - 1;
for(i = 0; i < len; i++) {
decimal += pow(2, power);
power--;
}
printf("Decimal value is %d\n", decimal);
return 0;
}
প্রোগ্রাম: ১২.১
প্রোগ্রাম কম্পাইল করে রান করো। ইনপুট যদি 10110 দাও, তাহলে আউটপুট কত আসে? আউটপুট আসে 31 কিন্তু আউটপুট তো আসা উচিত 22। তাহলে আমরা কোথাও ভুল করেছি। তোমরা নিজে নিজে ভুলটি বের করার চেষ্টা করো।
আমাদের তো আসলে pow(2, position)কে বাইনারি সংখ্যার ওই position-এর অঙ্কটি দিয়ে গুণ করার কথা, সেটি আমরা করতে ভুলে গেছি। অর্থাৎ আমাদের লিখতে হবে:
decimal += binary[i] * pow(2, power);
একটি ব্যাপার খেয়াল করছো তো? 10110-এর একের ঘরের অঙ্কটি আমাদের অ্যারের শেষ ক্যারেক্টার, আর ষোলোর ঘরের অঙ্কটি হচ্ছে অ্যারের প্রথম ক্যারেক্টার। অ্যারেতে সংখ্যাটি আছে এইভাবে: ['1', '0', '1', '1', '0']। তাই binary[0]-এর সঙ্গে গুণ হবে pow(2, 4), binary[1]-এর সঙ্গে গুণ হবে pow(2, 3), …, এভাবে binary[4]-এর সঙ্গে গুণ হবে pow(2, 0)। এখন প্রোগ্রামটি ঠিক করে নিয়ে তারপর চালাও। ইনপুট 10110-এর জন্য কী আউটপুট?
আমি তো আউটপুট দেখতে পাচ্ছি Decimal value is 1510। ভুলটি কোথায় হলো? সব তো ঠিকই করলাম। তোমরা আবার বিরক্ত হয়ে যাচ্ছ না তো? টেস্ট ক্রিকেট খেলার সময় যেমন ধৈর্যের প্রয়োজন, প্রোগ্রামিংও তেমনই ধৈর্যের খেলা।
ভুলটি যে decimal += binary[i] * pow(2, power); স্টেটমেন্টে হয়েছে তাতে কোনো সন্দেহ নেই। কারণ আমরা এখানেই একটু পরিবর্তন করেছি। লক্ষ করো, binary[i]-এর মান হয় '0' বা '1' (মানে ক্যারেক্টার '0' বা ক্যারেক্টার '1')। এখন কম্পিউটার '0' বলতে বোঝে 48 আর '1' বলতে বোঝে 49। ঝামেলাটা এখানেই হয়েছে। এখন এই '0'কে 0 আর '1'কে 1 বোঝাব কীভাবে?
'0' – '0' = 48 – 48 = 0।
'1' – '0' = 49 – 48 = 1।
বুদ্ধিটা দারুণ না? আমরা binary[i] না লিখে (binary[i] – '0') লিখলেই ঝামেলা শেষ। এবারে প্রোগ্রাম ঠিকঠাক কাজ করবে (যদি না তুমি নতুন কোনো ভুল করে থাকো)।
এবারে আমরা দেখব ডেসিমাল থেকে বাইনারিতে রূপান্তর। একটি উদাহরণের সাহায্যে পদ্ধতিটা দেখাই। ধরো 95কে বাইনারিতে রূপান্তর করতে হবে। এখন আমাদের বের করতে হবে n-এর সর্বোচ্চ মান, যেখানে 2n <= 95। দুইয়ের পাওয়ারগুলো হচ্ছে 1, 2, 4, 8, 16, 32, 64, 128, ...। এখানে আমরা দেখতে পাচ্ছি 64 < 95 বা 26 < 95। তাহলে n-এর মান 6। আর আমাদের বাইনারি সংখ্যাটি হবে সাত অঙ্কের (0 থেকে 6 মোট সাতটি অঙ্ক)। যেহেতু 64 < 95, তাই এই সংখ্যাটি নেওয়া যায়। তাহলে চৌষট্টির ঘরে (বাঁ থেকে প্রথম বা ডান থেকে সপ্তম) হবে 1 (1xxxxxx)। এখন n-এর মান 1 কমাই। 64 + 25 = 64 + 32 = 96, যা কিনা 95-এর চেয়ে বড়। তাই একে নেওয়া যাবে না। অতএব বত্রিশের ঘরে 0 বসাই (10xxxxx)। এবারে n-এর মান আবার এক কমাই, n-এর মান এখন 4। 64 + 24 = 64 + 16 = 80 < 95। সুতরাং ষোলোর ঘরে হবে 1 (101xxxx)। এখন n-এর মান এক কমাই, n = 3। 80 + 23 = 80 + 8 = 88 < 95। তাই আটের ঘরেও 1 বসবে (1011xxx)। এরপর একইভাবে, n = 2-এর জন্য 88 + 22 = 88 + 4 = 92 < 95। চারের ঘরেও 1 বসবে (10111xx)। তারপর n = 1, 92 + 21 = 92 + 2 = 94 < 95। দুইয়ের ঘরেও 1 (101111x)। এখন n = 0, 94 + 20 = 94 + 1 = 95। তাই একের ঘরেও 1। সুতরাং বাইনারি সংখ্যাটি হচ্ছে 1011111।
তোমরা এখন এই পদ্ধতিতে কোনো দশভিত্তিক সংখ্যাকে বাইনারিতে রূপান্তর করার
প্রোগ্রাম লিখে ফেলো এবং বিভিন্ন মান দিয়ে পরীক্ষা করে দেখো।
এখন একই কাজ আমরা একটু অন্যভাবে করব। নিচের টেবিলটি দেখো:
ভাগফল
|
ভাগশেষ
| |
95/ 2
|
47
|
1
|
47/2
|
23
|
1
|
23/2
|
11
|
1
|
11/2
|
5
|
1
|
5/2
|
2
|
1
|
2/2
|
1
|
0
|
1 /2
|
0
|
1
|
এবারে ভাগশেষ কলামের অঙ্কগুলো শেষ থেকে প্রথম ক্রমে লেখলেই আমরা বাইনারি নম্বরটা পেয়ে যাব: 1011111। আর ভাগের কাজটি আমরা ততক্ষণ করব যতক্ষণ না ভাগফল 0 পাচ্ছি।
এই পদ্ধতিতেও তোমরা ডেসিমাল থেকে বাইনারি রূপান্তরের জন্য একটি কোড লিখে ফেলো। রূপান্তরের কোডটি main ফাংশনে না করে আলাদা একটি ফাংশনে করবে।
কিছু প্রোগ্রামিং সমস্যা-
এই অধ্যায়ে আমরা কয়েকটি সহজ সমস্যা দেখব ও সমাধানের চেষ্টা করব।
আমাদের প্রথম সমস্যা হচ্ছে, বিভিন্ন ধরনের আকৃতি তৈরি করা। নিচের ছবিগুলো দেখো।
তোমাদের চারটি প্রোগ্রাম লিখতে হবে এই
চার ধরনের আকৃতি তৈরি করার জন্য। কেবল printf ফাংশন ব্যবহার করলেই হবে না,
লুপ ব্যবহার করতে হবে। তাহলে লুপ বা নেস্টেড লুপ, এবং 'c' ও ' ' (স্পেস
ক্যারেক্টার) প্রিন্ট করে তোমরা প্রোগ্রামগুলো লিখে ফেলতে পারো। আরও
খেলাধুলা করার ইচ্ছা হলে আরও নানান রকম আকৃতি তৈরির চেষ্টা করতে পার।
প্যালিন্ড্রোম (palindrome) কী
জিনিস, তোমরা জান? কোনো শব্দকে উল্টাভাবে (মানে শেষ থেকে শুরু) লিখলে যদি
সেটি আর নতুন শব্দটি একই রকম হয় তবে সেটি একটি প্যালিন্ড্রোম। যেমন: madam।
এটিকে শেষ থেকে শুরু পর্যন্ত লিখলেও madam হবে। এখন একটি প্রোগ্রাম লিখব
যেটিতে কোনো শব্দ ইনপুট দিলে সেটি প্যালিন্ড্রোম কি না বলে দেবে। তবে
প্রোগ্রাম লেখার আগে তোমাদের জন্য ইউটিউব থেকে দুটি ভিডিও:
এজন্য আমরা কী করতে পারি? প্রথমে
শব্দটি স্ট্রিং হিসেবে একটি অ্যারেতে ইনপুট নেব। তারপর আরেকটি অ্যারেতে
সেটি উল্টাভাবে রাখব। তারপর যদি দুটি একই স্ট্রিং হয়, তবে সেটি
প্যালিন্ড্রোম। তাহলে প্রোগ্রামটি লিখে ফেলি:
#include <stdio.h>
#include <string.h>
int main()
{
char word[80], reverse_word[80];
int i, j, len;
scanf("%s", word);
len = strlen(word);
for(i = 0, j = len - 1; i < len; i++, j--) {
reverse_word[i] = word[j];
}
reverse_word[i] = '\0';
printf("%s\n", reverse_word);
if (0 == strcmp(word, reverse_word)) {
printf("%s is a palindrome.\n", word);
}
else {
printf("%s is not a palindrome.\n", word);
}
return 0;
}
প্রোগ্রাম: ১৩.১
কী মজা! আমি প্রোগ্রামটি লিখে দিলাম।
তবে আমি এখানে বেশ কিছু বোকামি করেছি, যার মধ্যে অন্যতম হচ্ছে একটি
অতিরিক্ত অ্যারে ব্যবহার করা। সুতরাং তোমাদের এখন প্রোগ্রামটি এমনভাবে
লিখতে হবে, যাতে কেবল একটি অ্যারে ব্যবহার করেই কাজ হয়। আর তখন strcmp
ফাংশনটিরও দরকার হবে না। প্রোগ্রামটি লিখতে সময় বেশি লাগতে পারে, লাগুক,
অসুবিধা নেই। তবে লিখতে হবে ঠিকঠাক, এটিই হলো কথা।
তোমরা তো ফ্যাক্টরিয়াল (factorial)
জিনিসটির সঙ্গে পরিচিত? এটি একটি গাণিতিক অপারেশন যা কোনো ধনাত্মক
পূর্ণসংখ্যার ক্ষেত্রে ব্যবহার করা যায়। n একটি ধনাত্মক পূর্ণ সংখ্যা
হলে-এর ফ্যাক্টরিয়ালকে প্রকাশ করা হয় n! দিয়ে এবং n! = n * (n – 1) * (n –
2) * … * 3 * 2 * 1। যেমন 4! = 4 * 3 * 2 * 1 = 24। আবার 6! = 6 * 5 * 4 * 3
* 2 * 1 = 720। 1! = 1। 0! = 1 (0-এর ক্ষেত্রে ব্যতিক্রমটি লক্ষ করো, কিছু
বিশেষ সুবিধার জন্য 0-এর ফ্যাক্টরিয়ালের মান 1 ধরা হয়)। এখন তোমরা কোনো
ধনাত্মক পূর্ণসংখ্যার ফ্যাক্টরিয়াল বের করার প্রোগ্রামটি লিখে ফেলো। সহজ
প্রোগ্রাম, একটি লুপ ব্যবহার করেই করা যায়। এখন বিভিন্ন সংখ্যা দিয়ে
প্রোগ্রামটি টেস্ট করে দেখো ফ্যাক্টরিয়াল ঠিকঠাক বের করতে পারে কি না।
প্রোগ্রামে তুমি যদি ডাটা টাইপ int ব্যবহার করে থাক তবে 12-এর চেয়ে বড় কোনো
পূর্ণ সংখ্যার ফ্যাক্টরিয়ালের মান ঠিকমতো দেখাবে না (ক্যালকুলেটরে করে
মিলিয়ে দেখতে পারো)। কারণ হচ্ছে 12-এর চেয়ে বড় কোনো পূর্ণ সংখ্যার জন্য সেই
সংখ্যার ফ্যাক্টরিয়ালের মান রেঞ্জের বাইরে চলে যায়।
এখন তোমাদের একটি মজার সমস্যা সমাধান
করতে হবে। কোনো পূর্ণসংখ্যা n (যেখানে 1 < n < 100, মানে n-এর মান 2
থেকে 99 পর্যন্ত হতে পারে)-এর ফ্যাক্টরিয়ালকে মৌলিক সংখ্যার গুণফল হিসেবে
প্রকাশ করলে কোন মৌলিক সংখ্যা কতবার আছে সেটি বের করতে হবে। যেমন, আমরা
জানি, 5! = 120 = 2 * 2 * 2 * 3 * 5। এখানে 2 আছে 3 বার, 3 আছে 1 বার আর 5
আছে 1 বার। তাই ইনপুট 5 হলে আউটপুট হবে: 5! = (2, 3), (3, 1), (5, 1)।
তোমরা কি একটি ব্যাপার বুঝতে পারছ যে শুরুতে n-এর ফ্যাক্টরিয়ালের মান বের
করে তারপর মৌলিক উৎপাদকে ভাঙতে গেলে একটি ঝামেলা হয়ে যাবে? কারণ n-এর মান
সর্বোচ্চ হতে পারে 99 আর ইন্টিজারে তো 12-এর চেয়ে বড় কোনো সংখ্যার
ফ্যাক্টরিয়ালের মান রাখা যায় না। আসলে এই প্রোগ্রামের জন্য n!-এর মান বের
করার কোনো দরকার নেই। শুধু একটু গাণিতিক যুক্তি-বুদ্ধি খাটাও। আর 2 থেকে 99
পর্যন্ত মৌলিক সংখ্যাগুলো একটি অ্যারেতে রেখে নাও। প্রোগ্রামটি ঠিকভাবে
করতে তোমাদের অনেকেরই দু-তিন দিন সময় লেগে যেতে পারে, এতে হতাশ হওয়ার কিছু
নেই।
এখন আমরা একটি প্রোগ্রাম লিখব। যার উদ্দেশ্য হবে কোনো অ্যারেতে কিছু সংখ্যা থাকলে সেগুলোকে ছোট থেকে বড় ক্রমে সাজানো। যেমন, কোনো অ্যারে যদি এমন হয়: int ara[] = {3, 1, 5, 2, 4}, তবে আমাদের প্রোগ্রাম সেই অ্যারের সংখ্যাগুলো এমনভাবে সাজাবে, যাতে ara[] = {1, 2, 3, 4, 5} হয়।
প্রোগ্রামটি একটু পরে লিখব, তার আগে ঠিক করে নেই যে সেটি কীভাবে কাজ করবে। তোমার কাছে পাঁচটি সংখ্যা আছে: 3, 1, 5, 2, 4। ছোট থেকে বড় ক্রমে সাজাতে হবে। তুমি প্রথমে কী করবে? প্রথমে সবচেয়ে ছোট সংখ্যাটি খুঁজে বের করে তাকে শুরুতে লিখবে: 1। তখন বাকি থাকে চারটি সংখ্যা: 3, 5, 2, 4। এখন এই চারটির মধ্যে সবচেয়ে ছোট সংখ্যাটি 1-এর পরে লিখবে: 1, 2। বাকি রইল 3, 5, 4। এদের মধ্যে সবচেয়ে ছোট 3। তাই তুমি লিখবে : 1, 2, 3। এখন বাকি 5, 4। এই দুটি সংখ্যার মধ্যে সবচেয়ে ছোট 4। সেটি তুমি 3-এর পরে লিখবে: 1, 2, 3, 4। এখন বাকি একটি সংখ্যা, 5। সেটি তুমি 4-এর পরে লিখবে। 1, 2, 3, 4, 5। তোমার সাজানোর কাজ হয়ে গেল। একে সর্টিং (sorting) বলে। বিভিন্ন উপায়ে এটি করা যায়। তবে আমরা একটি সহজ-সরল উপায়ে করলাম।
আমরা যেভাবে কাজটি করেছি, সেটি উইকিপিডিয়াতে চমৎকার একটা অ্যানিমেশনের সাহায্যে দেখানো হয়েছে। অ্যানিমেশনটি একবার দেখলে বোঝা কঠিন, তাই আমার পরামর্শ হচ্ছে কমপক্ষে চার-পাঁচবার এটি দেখো।
এখন প্রোগ্রামটি লিখব কীভাবে?
প্রথমে একটি অ্যারেতে সংখ্যাগুলো রাখো: int ara1[] = {3, 1, 5, 2, 4};
এখন আরেকটি অ্যারে নাও: int ara2[5];
অ্যারেটি এখনো খালি। তাই একটি ভেরিয়েবলে ইনডেক্স 0 লিখে রাখো। int index_2 = 0;
এখন একটি একটি করে ara2তে সংখ্যাগুলো রাখতে হবে। তার জন্য একটি লুপ দরকার।
for(index_2 = 0; index_2 < 5; index_2++) // মানে 0 থেকে 4 পর্যন্ত প্রতিটি ঘরে আমরা সংখ্যা বসাব। এই লুপের ভেতরে আরেকটি লুপ দরকার যেটি দিয়ে আমরা ara1-এর সবচেয়ে ছোট সংখ্যা খুঁজে বের করব।
minimum = 100000; // এমন একটি বড় সংখ্যা অ্যাসাইন করলাম যেটি ara1-এর যেকোনো সংখ্যার চেয়ে বড়।
for (i = 0; i < 5; i++) {
if (ara1[i] < minimum) {
minimum = ara1[i];
}
}
এখন ara1-এর ক্ষুদ্রতম সংখ্যাটি minimum এ চলে এল।
সেটি এখন ara2 তে রাখি: ara2[index_2] = minimum।
সবশেষে ara2-এর সব সংখ্যা প্রিন্ট করে দেখব।
এবারে চলো, পুরো প্রোগ্রামটি লিখে কম্পাইল ও রান করে দেখি আউটপুট কী আসে।
এখন আমরা একটি প্রোগ্রাম লিখব। যার উদ্দেশ্য হবে কোনো অ্যারেতে কিছু সংখ্যা থাকলে সেগুলোকে ছোট থেকে বড় ক্রমে সাজানো। যেমন, কোনো অ্যারে যদি এমন হয়: int ara[] = {3, 1, 5, 2, 4}, তবে আমাদের প্রোগ্রাম সেই অ্যারের সংখ্যাগুলো এমনভাবে সাজাবে, যাতে ara[] = {1, 2, 3, 4, 5} হয়।
প্রোগ্রামটি একটু পরে লিখব, তার আগে ঠিক করে নেই যে সেটি কীভাবে কাজ করবে। তোমার কাছে পাঁচটি সংখ্যা আছে: 3, 1, 5, 2, 4। ছোট থেকে বড় ক্রমে সাজাতে হবে। তুমি প্রথমে কী করবে? প্রথমে সবচেয়ে ছোট সংখ্যাটি খুঁজে বের করে তাকে শুরুতে লিখবে: 1। তখন বাকি থাকে চারটি সংখ্যা: 3, 5, 2, 4। এখন এই চারটির মধ্যে সবচেয়ে ছোট সংখ্যাটি 1-এর পরে লিখবে: 1, 2। বাকি রইল 3, 5, 4। এদের মধ্যে সবচেয়ে ছোট 3। তাই তুমি লিখবে : 1, 2, 3। এখন বাকি 5, 4। এই দুটি সংখ্যার মধ্যে সবচেয়ে ছোট 4। সেটি তুমি 3-এর পরে লিখবে: 1, 2, 3, 4। এখন বাকি একটি সংখ্যা, 5। সেটি তুমি 4-এর পরে লিখবে। 1, 2, 3, 4, 5। তোমার সাজানোর কাজ হয়ে গেল। একে সর্টিং (sorting) বলে। বিভিন্ন উপায়ে এটি করা যায়। তবে আমরা একটি সহজ-সরল উপায়ে করলাম।
আমরা যেভাবে কাজটি করেছি, সেটি উইকিপিডিয়াতে চমৎকার একটা অ্যানিমেশনের সাহায্যে দেখানো হয়েছে। অ্যানিমেশনটি একবার দেখলে বোঝা কঠিন, তাই আমার পরামর্শ হচ্ছে কমপক্ষে চার-পাঁচবার এটি দেখো।
এখন প্রোগ্রামটি লিখব কীভাবে?
প্রথমে একটি অ্যারেতে সংখ্যাগুলো রাখো: int ara1[] = {3, 1, 5, 2, 4};
এখন আরেকটি অ্যারে নাও: int ara2[5];
অ্যারেটি এখনো খালি। তাই একটি ভেরিয়েবলে ইনডেক্স 0 লিখে রাখো। int index_2 = 0;
এখন একটি একটি করে ara2তে সংখ্যাগুলো রাখতে হবে। তার জন্য একটি লুপ দরকার।
for(index_2 = 0; index_2 < 5; index_2++) // মানে 0 থেকে 4 পর্যন্ত প্রতিটি ঘরে আমরা সংখ্যা বসাব। এই লুপের ভেতরে আরেকটি লুপ দরকার যেটি দিয়ে আমরা ara1-এর সবচেয়ে ছোট সংখ্যা খুঁজে বের করব।
minimum = 100000; // এমন একটি বড় সংখ্যা অ্যাসাইন করলাম যেটি ara1-এর যেকোনো সংখ্যার চেয়ে বড়।
for (i = 0; i < 5; i++) {
if (ara1[i] < minimum) {
minimum = ara1[i];
}
}
এখন ara1-এর ক্ষুদ্রতম সংখ্যাটি minimum এ চলে এল।
সেটি এখন ara2 তে রাখি: ara2[index_2] = minimum।
সবশেষে ara2-এর সব সংখ্যা প্রিন্ট করে দেখব।
এবারে চলো, পুরো প্রোগ্রামটি লিখে কম্পাইল ও রান করে দেখি আউটপুট কী আসে।
#include <stdio.h>
int main()
{
int ara1[] = {3, 1, 5, 2, 4};
int ara2[5];
int i, minimum, index_2;
for (index_2 = 0; index_2 < 5; index_2++) {
minimum = 10000;
for (i = 0; i < 5; i++) {
if (ara1[i] < minimum) {
minimum = ara1[i];
}
}
ara2[index_2] = minimum;
}
for (i = 0; i < 5; i++) {
printf("%d\n", ara2[i]);
}
return 0;
}
প্রোগ্রাম: ১৩.২
কী সুন্দর প্রোগ্রাম! আউটপুট কী? আউটপুটও খুব সুন্দর, একে একে পাঁচটি 1।
1
1
1
1
1
কিন্তু আমরা তো এমন আউটপুট চাইনি। কোথাও গোলমাল হয়েছে। এখন আমার কোডে দেখো তো কোনো ভুল বের করা যায় কি না।
একটি ঝামেলা হয়েছে। ভেতরের লুপে
(যেখানে সবচেয়ে ছোট সংখ্যা বের করা হয়) কিন্তু সব সময়ই minimum-এর মান 1
আসবে, কারণ 1 হচ্ছে ওই পাঁচটির মধ্যে সবচেয়ে ছোট সংখ্যা। এজন্য দ্বিতীয়
অ্যারেতে পাঁচটি সংখ্যাই 1 হয়ে যাচ্ছে। তাই আমরা যখন minimum বের করব, তখন
অ্যারের যেই ঘরে সবচেয়ে ছোট সংখ্যা পাব সেই ঘরের মান একটি অনেক বড় সংখ্যা
দিয়ে দেব। এজন্য একটি ভেরিয়েবল রাখি minimum_index। আর লুপটি এখন এমন হবে:
minimum = 10000;
for (i = 0; i < 5; i++) {
if (ara1[i] < minimum) {
minimum = ara1[i];
minimum_index = i;
}
}
এখন minimum-এর মান আমরা পেয়ে গেছি এবং সেই সঙ্গে এটিও জানি যে এটি আসলে আছে ara1[minimum_index] ঘরে।
ara1[minimum_index] = 10000;
তাহলে প্রোগ্রামটি ঠিক করে আবার চালাই:
#include <stdio.h>
int main()
{
int ara1[] = {3, 1, 5, 2, 4};
int ara2[5];
int i, minimum, index_2, minimum_index;
for (index_2 = 0; index_2 < 5; index_2++) {
minimum = 10000;
for (i = 0; i < 5; i++) {
if (ara1[i] < minimum) {
minimum = ara1[i];
minimum_index = i;
}
}
ara1[minimum_index] = 10000;
ara2[index_2] = minimum;
}
for (i = 0; i < 5; i++) {
printf("%d\n", ara2[i]);
}
return 0;
}
প্রোগ্রাম: ১৩.৩
এখন প্রোগ্রামটি আউটপুট ঠিকঠাক দেখাবে।
আচ্ছা, সব কাজই তো আমি করে দিলাম। তোমাদের কাজটি কী? তোমাদের কাজ হবে
প্রোগ্রামটি এমনভাবে লেখা যাতে দ্বিতীয় অ্যারের প্রয়োজন না হয়। শুরুতে যে
অ্যারেটি আছে তার ভেতরেই সর্টিং করতে হবে। এজন্য সবচেয়ে ছোট সংখ্যাটি
অ্যারের প্রথম ঘরে নিয়ে আসো আর যে ঘর থেকে সবচেয়ে ছোট সংখ্যা পেয়েছ সেখানে
প্রথম ঘরের সংখ্যাটি রাখো। এখন তোমার অ্যারের প্রথম ঘরে আছে সবচেয়ে ছোট
সংখ্যা। এবারে বাকি চারটি ঘরের মধ্যে সবচেয়ে ছোট সংখ্যাটি অ্যারের দ্বিতীয়
ঘরে রাখো এবং যে ঘর থেকে ওই সংখ্যাটি পেয়েছ সেখানে দ্বিতীয় ঘরের সংখ্যাটি
রাখো। আর কিছু বলা যাবে না।
রোবট নিয়ে এখন আমরা একটি প্রোগ্রাম
লিখব। কোনো একটি N x N গ্রিডে একটি রোবট আছে। শুরুতে তার একটি অবস্থান আছে।
আমরা সেটিকে কিছু কমান্ড দেব, এক ঘর ডানে, বাঁয়ে, ওপরে ও নিচে যাওয়ার
কমান্ড।
(0, 0)
|
(0, 1)
|
(0, 2)
|
(0, 3)
|
(0, 4)
|
(0, 5)
|
(0, 6)
|
(0, 7)
|
(0, 8)
|
(1, 0)
|
(1, 2)
| |||||||
(2, 0)
|
(2, 1)
|
R (2, 2)
|
(2, 3)
| |||||
(3, 0)
|
(3, 2)
| |||||||
(4, 0)
| ||||||||
(5, 0)
| ||||||||
(6, 0)
| ||||||||
(7, 0)
| ||||||||
(8, 0)
|
(8, 8)
|
গ্রিডটি দেখো। ওপরের একেবারে বাঁ দিকের
ঘর হচ্ছে (0, 0)। ওপরের একেবারে ডানদিকের ঘর হচ্ছে (0, 8)। নিচের একেবারে
বাঁ দিকের ঘর হচ্ছে (8, 0)। নিচের একেবারে ডান দিকের ঘর হচ্ছে (8, 8)। ধরা
যাক, এই মুহূর্তে রোবটটি আছে (2, 2) ঘরে। এক ঘর ওপরে যেতে বললে সে যাবে (1,
2) ঘরে। নিচে যেতে বললে যাবে (3, 2) ঘরে। ডানে আর বাঁয়ে যেতে বললে
যথাক্রমে (2, 3) ও (2, 1) ঘরে যাবে। কমান্ডগুলো হচ্ছে U (up), D (down), L
(left), R (right), S (stop)। এখন তোমাকে যদি শুরুর অবস্থান আর কমান্ডগুলো
বলে দিই, তাহলে রোবটের শেষ অবস্থান (stop করার পর অবস্থান) বের করতে হবে।
তোমরা কি প্রোগ্রামটি নিজে লিখার জন্য কিছুক্ষণ চেষ্টা করবে?
তোমরা নিশ্চয়ই বুঝতে পারছ যে একটি
2-D অ্যারে দরকার হবে এই প্রোগ্রামে। আসলে কিন্তু এখানে অ্যারের কোনোই
দরকার নেই। এটি সাধারণ যোগ-বিয়োগের প্রোগ্রাম। মনে করি, শুরুর অবস্থান
হচ্ছে (x, y)। এখন U কমান্ড দিলে একঘর ওপরে যাবে, তখন x-এর মান এক কমে
যাবে, y-এর মানের কোনো পরিবর্তন হবে না। D কমান্ড দিলে এক ঘর নিচে যাবে,
তখন x-এর মান এক বেড়ে যাবে, y-এর মানের কোনো পরিবর্তন হবে না। R কমান্ড
দিলে y-এর মান এক বাড়বে, x-এর মান অপরিবর্তিত থাকবে। L কমান্ড দিলে y-এর
মান এক কমবে, x-এর মান অপরিবর্তিত থাকবে। তাহলে আমাদের পুরো প্রোগ্রামটি
দাঁড়াবে এই রকম:
#include <stdio.h>
int main()
{
int x, y;
char c;
printf("Please enter the initial position: ");
scanf("%d %d", &x, &y);
while (1) {
scanf("%c", &c);
if (c == 'S') {
break;
}
else if (c == 'U') {
x--;
}
else if (c == 'D') {
x++;
}
else if (c == 'R') {
y++;
}
else if (c == 'L') {
y--;
}
}
printf("Final position of the robot is: %d, %d\n", x, y);
return 0;
}
প্রোগ্রাম: ১৩.৪
আউটপুট কী হবে সেটি নির্ভর করবে তোমার ইনপুটের ওপর। যেমন:
Please enter the initial position: 2 2
D
R
D
R
S
Final position of the robot is: 4, 4
বেশ সহজ সরল প্রোগ্রাম। কিন্তু এখন যদি
বলি যে গ্রিডে কিছু কিছু ঘরে যাওয়া নিষেধ এবং ওই ঘরগুলোতে যেতে বললে রোবটটি
কিছুই করবে না (অর্থাৎ ওই কমান্ডকে উপেক্ষা করবে), তখন আমরা প্রোগ্রামটি
কীভাবে লিখব? যেমন একটি উদাহরণ দিই। ধরা যাক, (0, 4) ঘরটি নিষিদ্ধ
(blocked)। যদি রোবটের অবস্থান হয় (0, 3) ঘরে এবং তাকে 'R' কমান্ড দেওয়া
হয়, তখন তার অবস্থানের কোনো পরিবর্তন হবে না। কারণ এক ঘর ডানে (মানে (0, 4)
ঘরে) যাওয়া সম্ভব নয়।
এই সমস্যার সমাধান করতে যে প্রোগ্রামটি
লিখতে হবে, তাতে কিন্তু একটি 2-D অ্যারে ব্যবহার করতে হবে। এই অ্যারের
সাহায্যে আমরা বুঝব যে কোন ঘরে যাওয়া যাবে আর কোন ঘরে যাওয়া যাবে না। সেটি
কীভাবে? খুবই সহজ। যেসব ঘরে যাওয়া যাবে অ্যারের ওই ঘরগুলোতে 1 আর যেসব ঘরে
যাওয়া যাবে না সেগুলোতে 0 রাখব।
প্রথমে 10 x 10 গ্রিডের জন্য একটি 2-D অ্যারে ডিক্লেয়ার করি:
int grid[10][10];
তারপর শুরুতে ধরে নিই সব ঘরে যাওয়া যাবে।
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j ++) {
grid[i][j] = 1;
}
}
এখন কোন কোন ঘরগুলোতে যাওয়া যাবে না তা ব্যবহারকারীর কাছ থেকে ইনপুট নিই:
printf("Please enter the number of blocked cells: ");
scanf("%d", &n);
printf("Now enter the cells: ");
for (i = 0; i < n; i++) {
scanf("%d %d", &x, &y);
grid[x][y] = 0;
}
এখন কোনো ঘরে যাওয়া যাবে কি না, সেটি বোঝার জন্য একটি শর্ত পরীক্ষা করলেই হবে।
if (grid[x][y] == 1) {
যদি সত্য হয়, তবে (x, y) ঘরে যাওয়া যাবে।
}
এখন তোমরা সম্পূর্ণ প্রোগ্রামটি নিজে নিজে লিখে ফেলো।
শেষের শুরু-
আমরা
বইয়ের শেষ অধ্যায়ে চলে এসেছি। তোমরা যদি আগের অধ্যায়গুলো ঠিকমতো পড়ে থাকো,
উদাহরণগুলো নিজে নিজে কম্পিউটারে চালিয়ে দেখে থাকো এবং যখনই আমি তোমাদেরকে
কোনো প্রোগ্রাম নিজে লিখতে বলেছি, সেগুলো নিজে লিখার চেষ্টা করে থাকো,
তাহলে তোমাকে অভিনন্দন! তুমি প্রোগ্রামিং শেখার জন্য প্রস্তুত হয়ে গেছ। যদি
বলতে পারতাম তুমি প্রোগ্রামিং শিখে ফেলেছ তবে তোমাদেরও ভালো লাগত, আমারও
ভালো লাগত, কিন্তু মিথ্যা কথা বলে কী লাভ?
প্রোগ্রামিং হচ্ছে চর্চার বিষয়। মুখস্থ করে পরীক্ষায় অনেক ভালো রেজাল্ট করা যায়, এমনকি কলেজ-বিশ্ববিদ্যালয়ে প্রোগ্রামিং পরীক্ষাতেও মুখস্থ করে অনেকেই বেশ ভালো নম্বর পায়। তবে এই ভালো নম্বর পাওয়ার সঙ্গে ভালো প্রোগ্রামার হওয়ার আসলে কোন সম্পর্ক নেই। প্রোগ্রামিং হচ্ছে একধরনের দক্ষতা (skill) এবং কেবল নিয়মিত অনুশীলনের মাধ্যমেই এই দক্ষতা অর্জন করা সম্ভব। এর জন্য ভালো ছাত্র হওয়ার দরকার নেই, জিনিয়াস হওয়ারও কোনো দরকার নেই। দরকার হচ্ছে প্রোগ্রামিংকে ভালোবাসা। যখন তুমি প্রোগ্রামিং করতে বসলে খাওয়াদাওয়ার কথা ভুলে যাবে, রাতে কোনো প্রোগ্রামিং সমস্যা নিয়ে কাজ শুরু করলে আর কিছুক্ষণ পরে দেখবে বাইরে ভোরের আলো ফুটছে, কিংবা ভুলে বাথরুমের স্যান্ডেল পরে ক্লাসে চলে যাবে, তখন বুঝবে যে তুমি প্রোগ্রামার হয়ে যাচ্ছ।
এখন তোমার উচিত হবে বইটি আরেকবার পড়া এবং সঙ্গে সঙ্গে প্রোগ্রামগুলো আবার করা। তারপর তোমরা আরও বেশি সি শিখতে চাইলে সি-এর কোন বই পড়তে পারো। তোমরা যদি প্রোগ্রামিং কন্টেস্টের ব্যাপারে উৎসাহী হও তবে সি প্লাস প্লাস (C++) শেখা শুরু করে দিতে পারো কোন বই থেকে। আবার জাভা (Java), সি শার্প (C#), পিএইচপি (PHP) কিংবা পাইথন (Python) শিখতে পারো। কোনোটি শিখতেই তেমন ঝামেলা পোহাতে হবে না কারণ তুমি প্রোগ্রামিংয়ের মৌলিক জিনিসগুলো এতক্ষণে আয়ত্বে এনে ফেলেছ। বই ও ওয়েবসাইটের তালিকা আমি পরিশিষ্ট অংশে লিখেছি।
একজন দক্ষ প্রোগ্রামার হতে গেলে যে জিনিসগুলো লাগবে তা হচ্ছে–
১) এক বা একাধিক প্রোগ্রামিং ল্যাঙ্গুয়েজে ভালো দখল,
২) ভালো একটি IDE ব্যবহারের দক্ষতা,
৩) প্রোগ্রামিংয়ের মৌলিক বিষয়গুলো সম্পর্কে স্বচ্ছ ধারনা,
৪) গণিত ও যুক্তিতে দক্ষতা,
৫) অবজেক্ট ওরিয়েনটেড প্রোগ্রামিংয়ে (OOP– Object Oriented Programming) দক্ষতা,
৬) ডাটা স্ট্রাকচার ও অ্যালগরিদমের মৌলিক জ্ঞান ও তা প্রয়োগের ক্ষমতা,
৭) যোগাযোগে দক্ষতা (Communication Skills),
৮) ইন্টারনেট ঘেঁটে অল্প সময়ে কোনো সমস্যার সমাধান বের করা বা দ্রুত কোন নতুন বিষয় শিখে নেওয়ার দক্ষতা,
৯) একটি সমস্যার পিছনে লেগে থাকার মানসিকতা,
১০) প্রোগ্রামিংয়ের প্রতি ভালোবাসা।
তোমাদের প্রোগ্রামিং জীবন আনন্দময় হোক, তোমাদের নিজের জীবন আনন্দময় হোক, তোমাদের কারণে তোমাদের আশেপাশের মানুষদের জীবন আনন্দময় হোক। সবাইকে শুভেচ্ছা।
প্রোগ্রামিং হচ্ছে চর্চার বিষয়। মুখস্থ করে পরীক্ষায় অনেক ভালো রেজাল্ট করা যায়, এমনকি কলেজ-বিশ্ববিদ্যালয়ে প্রোগ্রামিং পরীক্ষাতেও মুখস্থ করে অনেকেই বেশ ভালো নম্বর পায়। তবে এই ভালো নম্বর পাওয়ার সঙ্গে ভালো প্রোগ্রামার হওয়ার আসলে কোন সম্পর্ক নেই। প্রোগ্রামিং হচ্ছে একধরনের দক্ষতা (skill) এবং কেবল নিয়মিত অনুশীলনের মাধ্যমেই এই দক্ষতা অর্জন করা সম্ভব। এর জন্য ভালো ছাত্র হওয়ার দরকার নেই, জিনিয়াস হওয়ারও কোনো দরকার নেই। দরকার হচ্ছে প্রোগ্রামিংকে ভালোবাসা। যখন তুমি প্রোগ্রামিং করতে বসলে খাওয়াদাওয়ার কথা ভুলে যাবে, রাতে কোনো প্রোগ্রামিং সমস্যা নিয়ে কাজ শুরু করলে আর কিছুক্ষণ পরে দেখবে বাইরে ভোরের আলো ফুটছে, কিংবা ভুলে বাথরুমের স্যান্ডেল পরে ক্লাসে চলে যাবে, তখন বুঝবে যে তুমি প্রোগ্রামার হয়ে যাচ্ছ।
এখন তোমার উচিত হবে বইটি আরেকবার পড়া এবং সঙ্গে সঙ্গে প্রোগ্রামগুলো আবার করা। তারপর তোমরা আরও বেশি সি শিখতে চাইলে সি-এর কোন বই পড়তে পারো। তোমরা যদি প্রোগ্রামিং কন্টেস্টের ব্যাপারে উৎসাহী হও তবে সি প্লাস প্লাস (C++) শেখা শুরু করে দিতে পারো কোন বই থেকে। আবার জাভা (Java), সি শার্প (C#), পিএইচপি (PHP) কিংবা পাইথন (Python) শিখতে পারো। কোনোটি শিখতেই তেমন ঝামেলা পোহাতে হবে না কারণ তুমি প্রোগ্রামিংয়ের মৌলিক জিনিসগুলো এতক্ষণে আয়ত্বে এনে ফেলেছ। বই ও ওয়েবসাইটের তালিকা আমি পরিশিষ্ট অংশে লিখেছি।
একজন দক্ষ প্রোগ্রামার হতে গেলে যে জিনিসগুলো লাগবে তা হচ্ছে–
১) এক বা একাধিক প্রোগ্রামিং ল্যাঙ্গুয়েজে ভালো দখল,
২) ভালো একটি IDE ব্যবহারের দক্ষতা,
৩) প্রোগ্রামিংয়ের মৌলিক বিষয়গুলো সম্পর্কে স্বচ্ছ ধারনা,
৪) গণিত ও যুক্তিতে দক্ষতা,
৫) অবজেক্ট ওরিয়েনটেড প্রোগ্রামিংয়ে (OOP– Object Oriented Programming) দক্ষতা,
৬) ডাটা স্ট্রাকচার ও অ্যালগরিদমের মৌলিক জ্ঞান ও তা প্রয়োগের ক্ষমতা,
৭) যোগাযোগে দক্ষতা (Communication Skills),
৮) ইন্টারনেট ঘেঁটে অল্প সময়ে কোনো সমস্যার সমাধান বের করা বা দ্রুত কোন নতুন বিষয় শিখে নেওয়ার দক্ষতা,
৯) একটি সমস্যার পিছনে লেগে থাকার মানসিকতা,
১০) প্রোগ্রামিংয়ের প্রতি ভালোবাসা।
তোমাদের প্রোগ্রামিং জীবন আনন্দময় হোক, তোমাদের নিজের জীবন আনন্দময় হোক, তোমাদের কারণে তোমাদের আশেপাশের মানুষদের জীবন আনন্দময় হোক। সবাইকে শুভেচ্ছা।