Module ৫ · সিদ্ধান্ত নেওয়া
If, else if, else: পথ বেছে নেওয়া
এই lesson-এ যা শিখবে
if,if elseআরelse ifladder লিখতে পারবে, প্রতিটা body-র চারপাশে brace দিয়ে।- একটা ladder-কে trace table দিয়ে মিলিয়ে দেখতে পারবে: কোন input কোন পথে গেল।
- বলতে পারবে ladder-এর ক্রম কেন উত্তর বদলে দেয়, আর condition-এর ভিতরে একটা
=কেন bug।
Module 4-এ Amara leap year-এর নিয়মটা এক expression-এ লিখেছিল, আর ওটা 1 বা 0 ছাপত। ওর calendar app কিন্তু চাইছিল একটা বাক্য: "2024 has 366 days"।
Screen-এ একটা 1 দেখানো আর একটা বাক্য বলা এক জিনিস না। দুইটা বাক্যের একটা ছাপতে হলে program-কে বেছে নিতে হবে কোন লাইনটা চলবে।
এখন পর্যন্ত প্রতিটা program প্রতিটা লাইন চালিয়েছে, উপর থেকে নিচে, প্রতিবার। আজ রাস্তাটা দুই ভাগ হচ্ছে।
if: যে block শুধু উত্তর শূন্য না হলে চলে
if statement একটা প্রশ্ন করে। উত্তর সত্য হলেই শুধু একটা block চালায়। C-তে "সত্য" মানে "শূন্য না", Module 2-এর সেই নিয়ম।
if statement
if (condition) {
statements
}
ifহলো keyword, আর এর পরে সব সময় বন্ধনী আসে।conditionযেকোনো expression। শূন্য মানে মিথ্যা; বাকি সব মান সত্য।- Brace-এর ভিতরে থাকে body: যে লাইনগুলো শুধু condition সত্য হলে চলে।
- শেষ brace-এর পরে program নিজের মতো এগিয়ে যায়, প্রশ্নের উত্তর যা-ই হোক।
#include <stdio.h>
int main(void)
{
int stock = 7;
if (stock < 10) {
printf("reorder\n");
}
printf("stock checked\n");
return 0;
}
reorder
stock checked
7-কে 12 করে দাও, তখন শুধু দ্বিতীয় লাইনটা ছাপবে। Brace-এর ভিতরের printf বাদ পড়ে; পরেরটা পড়ে না।
মানে if program থামায় না, কোনো বেরোনোর রাস্তাও বাছে না। ও শুধু ঠিক করে একটা block চলবে কি না, তারপর program চলতে থাকে।
else: দুইটা পথের ঠিক একটা
else মিথ্যা উত্তরকে নিজের একটা block দেয়। দুইটা block-এর ঠিক একটাই চলে: কখনো দুইটাই না, কখনো একটাও-না-এমনও না।
#include <stdio.h>
int main(void)
{
int year = 2024;
int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
if (leap) {
printf("%d has 366 days\n", year);
} else {
printf("%d has 365 days\n", year);
}
return 0;
}
2024 has 366 days
Expression-টা Module 4-এর Amara-র লেখা, একটুও বদলায়নি। নতুন শুধু এটা যে ওর 1 বা 0 এখন ঠিক করে দেয় কোন printf চলবে।
তার মানে leap year-এর নিয়ম একটুও বদলায়নি। ওটা ছাপার মতো উত্তর থেকে কাজে লাগানোর মতো উত্তর হয়ে গেছে।
else if ladder: প্রথম যে condition সত্য, সে-ই জেতে
বাস্তবের বেশির ভাগ প্রশ্নের উত্তর দুইটার বেশি। C ওগুলোকে else if দিয়ে শিকলের মতো জোড়ে, আর এই শিকলটাকে সাধারণত ladder বলে।
else if ladder
if (condition_1) {
runs when condition_1 is true
} else if (condition_2) {
runs when condition_1 was false and condition_2 is true
} else {
runs when every condition above was false
}
- C উপর থেকে condition-গুলো একটা একটা করে পরীক্ষা করে।
- প্রথম যেটা সত্য, সেটার block চলে, আর তার নিচের সব পরীক্ষা বাদ পড়ে।
- শেষের
elseনা দিলেও চলে। না দিলে, কোনোটার সাথে না মেলা মানের জন্য কিছুই চলে না।
#include <stdio.h>
int main(void)
{
int marks = 72;
if (marks >= 80) {
printf("distinction\n");
} else if (marks >= 60) {
printf("merit\n");
} else if (marks >= 40) {
printf("pass\n");
} else {
printf("fail\n");
}
return 0;
}
merit
Ladder পড়ার হাতিয়ার হলো trace table। প্রতিটা input-এর জন্য একটা সারি, আর C আসলে যে পরীক্ষাগুলো চালিয়েছে সেগুলোর একটা কলাম।
| marks | যে পরীক্ষাগুলো চলল, ক্রমে | যে পথে গেল |
|---|---|---|
| 95 | >= 80 সত্য | distinction |
| 72 | >= 80 মিথ্যা, >= 60 সত্য | merit |
| 60 | >= 80 মিথ্যা, >= 60 সত্য | merit |
| 45 | >= 80 মিথ্যা, >= 60 মিথ্যা, >= 40 সত্য | pass |
| 12 | তিনটাই মিথ্যা | fail, মানে else |
72-এর সারির মাঝের কলামটা পড়ো। >= 40 পরীক্ষাটা চলেইনি। প্রথম সত্য উত্তরেই ladder থেমে গেছে।
এবার ক্রমের নিয়ম। Bob, যে সব সময় তাড়াহুড়া করে, একই ladder লেখে সবচেয়ে সহজ পরীক্ষা দিয়ে শুরু করে।
#include <stdio.h>
int main(void)
{
int marks = 95;
if (marks >= 40) {
printf("pass\n");
} else if (marks >= 60) {
printf("merit\n");
} else if (marks >= 80) {
printf("distinction\n");
} else {
printf("fail\n");
}
return 0;
}
pass
95-কে এর ভিতর দিয়ে trace করো। প্রথম পরীক্ষা, >= 40, সত্য, তাই ladder ওখানেই থামে। 62 আর 100-এর বেলাতেও একই কথা।
40 থেকে উপরের প্রতিটা মার্ক প্রথম পরীক্ষাতেই পাশ করে যায়, তাই merit আর distinction-এর block কখনো চলতেই পারে না। Compiler কিছুই বলে না; Bob কী বোঝাতে চেয়েছিল, সেটা ওর জানার উপায় নেই।
তাই যেকোনো ladder-এর নিয়মটা এই। একটা condition-এর পরিসর যদি আরেকটার পরিসরকে ভিতরে ধরে রাখে, ছোট পরিসরটাকে আগে বসাতে হবে। এখানে "80 আর তার বেশি" আছে "40 আর তার বেশি"-র ভিতরে, তাই ওটা উপরে যায়।
যেকোনো int-ই condition, আর 0 মানে মিথ্যা
if-এর পরের বন্ধনীতে তুলনা থাকতেই হবে, এমন না। সংখ্যা হয় এমন যেকোনো expression চলে, আর শুধু শূন্য মানে না।
#include <stdio.h>
int main(void)
{
int stock = 0;
int n = -7;
if (stock) {
printf("in stock\n");
} else {
printf("sold out\n");
}
if (n % 2) {
printf("%d is odd\n", n);
}
return 0;
}
sold out
-7 is odd
if (stock) মানে হুবহু if (stock != 0)। "কিছু আছে কি" ধরনের গণনার জন্য ছোট রূপটা পড়তে ভালো লাগে।
দ্বিতীয় পরীক্ষাটা একটু সূক্ষ্ম। -7 % 2 হলো -1, Module 4-এর sign-এর নিয়ম, আর -1 শূন্য না। তাই negative বিজোড় সংখ্যাতেও পরীক্ষাটা সত্য, যেখানে n % 2 == 1 হতো মিথ্যা।
তার মানে condition আলাদা কোনো জাতের মান না। ওটা একটা সংখ্যা, আর শুধু শূন্যই না বলে।
একটা সমান-চিহ্ন: যে bug compile হয়ে যায়
= একটা মান রাখে। == একটা প্রশ্ন করে। Condition-এর ভিতরে প্রথমটা প্রায় সব সময়ই টাইপের ভুল।
#include <stdio.h>
int main(void)
{
int marks = 35;
if (marks = 100) {
printf("full marks\n");
}
printf("marks is now %d\n", marks);
return 0;
}
full marks
marks is now 100
একসাথে দুইটা জিনিস ভুল হলো। Assignment-এর মান 100, যেটা শূন্য না, তাই পরীক্ষাটা সব সময় সত্য। আর ছাত্রটার আসল মার্কটা মুছে গেছে।
Playground এটা চুপচাপ compile করে, কারণ ওখানে -Wall নেই। নিজের মেশিনে GCC 12-এর gcc -Wall বলে warning: suggest parentheses around assignment used as truth value [-Wparentheses]।
তাই যে if তোমার দেওয়া প্রতিটা input-এই সত্য, ওখানে আগে একা একটা = খোঁজো।
প্রতিটা body-তে brace, আর যে bug সবাইকে কারণটা শিখিয়েছে
Body যদি একটাই statement হয়, C brace ছাড়া if লিখতে দেয়। এই track সেই ছাড় কখনো নেয় না, আর কারণটা এখানে।
2014-র February-তে Apple iOS আর macOS-এর TLS code-এর একটা fix ছাড়ে, যেটার নাম CVE-2014-1266। Server-এর signature যাচাই করা একটা function-এ brace ছাড়া একটা if-এর নিচে পরপর দুইটা হুবহু এক লাইন ছিল: goto fail;। প্রথম লাইনটাই শুধু if-এর। দ্বিতীয়টা প্রতিবার চলত, আসল যাচাইটা টপকে যেত, আর বলত সব ঠিক আছে। লোকে ওটাকে ডাকে "goto fail"।
শুধু printf দিয়েই একই ফাঁদ বানানো যায়।
#include <stdio.h>
int main(void)
{
int marks = 35;
if (marks >= 40)
printf("pass\n");
printf("certificate printed\n");
return 0;
}
certificate printed
Indentation দেখে মনে হয় দুইটা লাইনই if-এর। Compiler indentation-কে পাত্তাই দেয় না, তাই শুধু প্রথমটাই ওর।
Playground আবারও চুপ। নিজের মেশিনের gcc -Wall বলে warning: this 'if' clause does not guard... [-Wmisleading-indentation]। Brace থাকলে প্রশ্নটাই ওঠে না।
তাই brace সাজসজ্জা না। Body কোথায় শেষ, C-কে সেটা বলার একমাত্র উপায় ওটাই।
একটা condition, একটা body, আর উত্তর না হলে কিছুই না।
#include <stdio.h>
int main(void)
{
int celsius = 0;
scanf("%d", &celsius);
if (celsius < 0) {
printf("freezing\n");
}
return 0;
}
freezing
ওই output-টা -3 input-এর জন্য। 5 দিলে program কিছুই ছাপে না, আর ওকে ঠিক সেটাই করতে বলা হয়েছিল।
দুইটা পথ, আর negative সংখ্যা কোন পথে যাবে সেটা ঠিক করে Module 4-এর ভাগশেষের নিয়ম।
#include <stdio.h>
int main(void)
{
int n = 0;
scanf("%d", &n);
if (n % 2 == 0) {
printf("%d is even\n", n);
} else {
printf("%d is odd\n", n);
}
return 0;
}
-7 is odd
ওই output-টা -7 input-এর জন্য। 0-র সাথে মেলালে দুই sign-এই কাজ করে, কারণ -1 ভাগশেষও তো 0 না।
বয়স দেখে সিনেমার টিকিট। একটা পাহারা আগে অসম্ভব input বাদ দেয়, তারপর band-গুলো সবচেয়ে ছোট বয়স থেকে উপরে ওঠে।
#include <stdio.h>
int main(void)
{
int age = 0;
scanf("%d", &age);
if (age < 0) {
printf("invalid age\n");
} else if (age < 3) {
printf("ticket 0\n");
} else if (age < 12) {
printf("ticket 150\n");
} else if (age >= 60) {
printf("ticket 200\n");
} else {
printf("ticket 300\n");
}
return 0;
}
ticket 200
ওই output-টা 65 input-এর জন্য। এখানে পরীক্ষাগুলোয় <, তাই "3-এর কম" আছে "12-এর কম"-এর ভিতরে, আর ওটাই আগে আসে। নিয়ম সেই একটাই, শুধু উল্টো দিক থেকে পড়া।
এটা কোথায় কাজে লাগে
- পরীক্ষার ফল। বাংলাদেশের শিক্ষা বোর্ড একটা grade-এর table ছাপে: 80 বা তার বেশি A+, আর 33-এর নিচে F। যে program ওটা ছাপে, সেটা একটা
else ifladder, সবচেয়ে কড়া band আগে। - Thermostat। Linux kernel-এর
step_wisethermal governor তাপমাত্রাকে কয়েকটা trip point-এর সাথে মেলায়। কয়েকটাifপরপর ঠিক করে ঠান্ডা করা বাড়বে, কমবে, না একই থাকবে। - Input যাচাই। nginx web server
client_max_body_size-এর চেয়ে বড় request body-কে status 413 দিয়ে ফিরিয়ে দেয়। একটা তুলনা, একটা branch, আসল কাজ শুরুর আগেই। - Login পরীক্ষা। OpenSSH-এর server ভুল password-এর চেষ্টাগুলো গোনে, আর
MaxAuthTries-টা ভুলের পরে সংযোগ কেটে দেয়। ওটা একটা counter-এর উপরif।
যে ভুলগুলো সবাই করে
১. Condition-এ একটা সমান-চিহ্ন।
if (marks = 100) {
printf("full marks\n");
}
Playground-এ নীরব। নিজের মেশিনের gcc -Wall বলে warning: suggest parentheses around assignment used as truth value [-Wparentheses]। পরীক্ষাটা সব সময় সত্য, আর marks-এর মান মুছে যায়। == লেখো। জোরে টাইপ করলেই এটা হবে, কারণ দুইটা একই চাবি।
২. Condition-এর ঠিক পরেই একটা semicolon।
if (marks >= 40);
{
printf("pass\n");
}
Playground-এ নীরব। নিজের মেশিনের gcc -Wall জানায় this 'if' clause does not guard... [-Wmisleading-indentation], আর -Wextra যোগ করে suggest braces around empty body in an 'if' statement [-Wempty-body]। Semicolon-টাই একটা ফাঁকা body, তাই প্রতিটা মার্কেই "pass" ছাপে। ওটা মুছে দাও; if-এর লাইন কখনো semicolon দিয়ে শেষ হয় না।
৩. মুখে যে ক্রমে বলো, সেই ক্রমে চিহ্ন লেখা।
if (marks =< 40) {
printf("fail\n");
}
প্রতিটা command line-এ error, Playground-সহ: error: expected expression before '<' token। Operator-টা <=, কোণটা আগে। মুখে তো বলো "less than or equal", চিহ্নও ইংরেজি কথাটার ক্রমেই বসে।
৪. সবচেয়ে চওড়া পরীক্ষাটা সবার উপরে।
if (marks >= 40) {
printf("pass\n");
} else if (marks >= 80) {
printf("distinction\n");
}
কোনো command line-এই বার্তা নেই। দ্বিতীয় block কখনো চলতে পারে না, কারণ 80 ছোঁয়া প্রতিটা মার্ক আগেই প্রথম পরীক্ষায় পাশ করে গেছে। ছোট পরিসর আগে বসাও, আর বিশ্বাস করার আগে প্রতিটা band থেকে একটা করে input trace করো।
Alice বাংলাদেশের শিক্ষা বোর্ডের table দিয়ে report card ছাপে। 80 থেকে 100 হলো A+, 70 থেকে 79 A, 60 থেকে 69 A-, আর 50 থেকে 59 B। তারপর 40 থেকে 49 C, 33 থেকে 39 D, আর 0 থেকে 32 F।
Input. এক লাইনে একটা পূর্ণসংখ্যা marks।
Output. এক লাইনে grade-টা, আর marks যদি 0-র নিচে বা 100-র উপরে হয় তাহলে invalid।
Constraints. -1000 <= marks <= 1000।
Sample. Input 79 দিলে A। Input 101 দিলে invalid।
#include <stdio.h>
int main(void)
{
int marks = 0;
scanf("%d", &marks);
/* One guard for the impossible marks, then one rung per band. */
return 0;
}
grade-bands নামে গ্রেড হয়। Table-এর প্রতিটা সীমাই hidden test, দুই পাশ থেকেই।
Kenji তিনটা ল্যাপ দৌড়েছে, আর leaderboard শুধু ওর সেরা score দেখায়। দুইটা ল্যাপের score সমান হতে পারে।
Input. এক লাইনে তিনটা পূর্ণসংখ্যা।
Output. এক লাইনে তিনটার মধ্যে সবচেয়ে বড়টা।
Constraints. প্রতিটা মান -1000000000 থেকে 1000000000-এর মধ্যে।
Sample. Input 3 9 5 দিলে 9। Input 7 2 7 দিলে 7।
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int c = 0;
scanf("%d %d %d", &a, &b, &c);
/* Two separate ifs are enough. Equal values must not break it. */
return 0;
}
largest-of-three নামে গ্রেড হয়। Hidden test-গুলোয় সবচেয়ে বড় মানটা তিনটা জায়গাতেই ঘুরে আসে।
Amara-র calendar-এর যেকোনো বছরের জন্য একটা সোজা উত্তর দরকার: leap year, না না?
Input. এক লাইনে একটা পূর্ণসংখ্যা y।
Output. এক লাইনে: y leap year হলে yes, নয়তো no।
Constraints. 1 <= y <= 9999।
Sample. Input 1900 দিলে no। Input 2000 দিলে yes।
#include <stdio.h>
int main(void)
{
int y = 0;
scanf("%d", &y);
/* The Module 4 expression decides; an if else prints the word. */
return 0;
}
আলাদা করে গ্রেড হয় না। Problem lesson-এর date-check এই নিয়মটাই একটা বড় প্রশ্নের ভিতরে ব্যবহার করে।
Zara তিনটা দৈর্ঘ্য দিয়ে একটা geometry app test করে। প্রতিটা দৈর্ঘ্য বাকি দুইটার যোগফলের চেয়ে ছোট হতে হবে, নইলে triangle নেই। যোগফলের সমান হলে আকারটা চ্যাপ্টা, ওটা গোনা হয় না।
Input. এক লাইনে তিনটা পূর্ণসংখ্যা a, b আর c।
Output. এক লাইনে: not a triangle, নইলে equilateral, isosceles বা scalene।
Constraints. 1 <= a, b, c <= 1000000000।
Sample. Input 2 2 3 দিলে isosceles। Input 1 2 3 দিলে not a triangle।
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int c = 0;
scanf("%d %d %d", &a, &b, &c);
/* Is it a triangle at all? Then the narrowest kind first. */
return 0;
}
triangle-kind নামে গ্রেড হয়। ধাপগুলোর ক্রমটাই পুরো কঠিন অংশ।
সচরাচর যে প্রশ্নগুলো আসে
Body এক লাইনের হলেও কি সত্যিই brace লাগবে?
C বাধ্য করে না। এই track করে, কারণ পরে কেউ যে লাইনটা যোগ করবে, সেটা দেখে মনে হবে পাহারার ভিতরে, অথচ থাকবে না। দুইটা অক্ষর তার তুলনায় খুব সস্তা দাম।
else ifকি আলাদা একটা keyword?না। ওটা একটা
else, যার body ঘটনাচক্রে আরেকটাif। দুইটা শব্দ এক লাইনে লেখা সাজানোর অভ্যাস মাত্র, আর প্রতিটা C programmer ladder-টা ওভাবেই পড়ে।আলাদা আলাদা কয়েকটা
ifনা লিখে ladder কেন?আলাদা statement-গুলো সবগুলোই পরীক্ষা হয়, তাই একাধিক block চলতে পারে। 95 মার্কের জন্য তিনটা আলাদা পরীক্ষা distinction, merit আর pass তিনটাই ছাপত। Ladder প্রথম সত্য পরীক্ষাতেই থামে, তাই ঠিক একটা block চলে।
if (x)কি হুবহুif (x != 0)-এর মতো?হ্যাঁ, compiler-এর কাছে। "কিছু আছে কি" ধরনের flag বা গণনার জন্য ছোট রূপটা নাও। শূন্যটা যখন আসল একটা মান যার সাথে তুলনা করছ, তখন লম্বা রূপটা নাও।
লম্বা ladder কি program ধীর করে দেয়?
এখন মাপতে পারবে এমনভাবে না। Lesson 5 দেখাবে GCC একটা condition থেকে আসলে কী বানায়, আর সেটা প্রায়ই source দেখে যা মনে হয় তার চেয়ে কম।
মূল কথা
ifনিজের body চালায় শুধু condition শূন্য না হলে।elseমিথ্যা উত্তরকে নিজের body দেয়, আর দুইটার ঠিক একটাই চলে।- Ladder উপর থেকে পরীক্ষা করে, আর প্রথম সত্য condition-এই থামে।
- Trace table দেখায় কোন পরীক্ষাগুলো চলল; এক পরিসর আরেকটাকে ধরে রাখলে ছোটটা আগে।
- Condition-এর ভিতরে একা
=মান বসিয়ে দেয়, আর Playground ওটা নিয়ে চুপ থাকে। - প্রতিটা body-তে brace, এক লাইনের body-তেও।
এরপর একটা if ঢুকবে আরেকটা if-এর ভিতরে, আর একটা else ভুলে যাবে ও আসলে কার।
lesson ১ শেষ
শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।
পরেরটা: Nested condition আর ঝুলে থাকা else