কন্ট্রোল স্ট্রাকচার — সিলেকশন ও ইটারেশন
এই পাঠে যা শিখবেন
- সিলেকশন স্ট্রাকচার — if/else ও মাল্টি-ওয়ে সিলেকশন (switch/match) এর উদ্দেশ্য ও ব্যবহার
- pretest, posttest ও counter-controlled ইটারেশনের সংজ্ঞা ও আচরণগত পার্থক্য
- for-লুপ কীভাবে while-লুপ মেশিনারি দিয়ে সমতুল্যভাবে বাস্তবায়ন করা যায় — একটি ইউনিফাইং ইনসাইট
- Python দিয়ে
for_to_whileকনভার্সন এবং pretest/posttest আচরণগত পার্থক্য বাস্তবায়ন ও যাচাই
১ · সিলেকশন স্ট্রাকচার — if/else ও মাল্টি-ওয়ে সিলেকশন
সিলেকশন স্ট্রাকচারSelection Structureএকটি বুলিয়ান কন্ডিশনের ভিত্তিতে একাধিক বিকল্প এক্সিকিউশন পাথের মধ্যে একটি বেছে নেওয়ার কন্ট্রোল স্ট্রাকচার।
হলো এমন কন্ট্রোল স্ট্রাকচার, যা একটি বুলিয়ান কন্ডিশনের সত্য/মিথ্যা মানের উপর ভিত্তি করে প্রোগ্রামের
এক্সিকিউশনকে ভিন্ন ভিন্ন পথে পাঠায়। সবচেয়ে মৌলিক রূপ হলো if/else — একটি কন্ডিশন সত্য হলে
একটি ব্লক, মিথ্যা হলে অন্য একটি ব্লক এক্সিকিউট হয়। যখন একই ভ্যারিয়েবলের অনেকগুলো ডিসক্রিট (discrete) মান
নিয়ে সিদ্ধান্ত নিতে হয়, গভীরভাবে নেস্টেড if/elif/else চেইন লেখা বিশ্রী ও পড়তে কঠিন হয়ে যায় —
তাই বেশিরভাগ ভাষা একটি মাল্টি-ওয়ে সিলেকশন কনস্ট্রাক্ট (যেমন switch বা
match) দেয়, যা একই কন্ডিশনাল লজিককে আরও পরিষ্কারভাবে প্রকাশ করার একটি সুবিধাজনক
(convenience) জেনারেলাইজেশন — এটি নতুন কোনো কম্পিউটেশনাল ক্ষমতা যোগ করে না, শুধু বহু-শাখার সিদ্ধান্তকে
পড়তে সহজ করে তোলে।
২ · ইটারেশন স্ট্রাকচার — pretest, posttest ও counter-controlled
ইটারেশনIterationএকটি কোড ব্লক বারবার এক্সিকিউট করার কন্ট্রোল স্ট্রাকচার — সাধারণত একটি লুপিং কনস্ট্রাক্ট দিয়ে প্রকাশ করা হয়। হলো একটি কোড ব্লক পুনরাবৃত্তি করার কন্ট্রোল স্ট্রাকচার। বাস্তবে তিন ধরনের ইটারেশন কনস্ট্রাক্ট দেখা যায়, এবং তাদের মধ্যে একটি গুরুত্বপূর্ণ আচরণগত পার্থক্য আছে —
কন্ডিশন প্রতি ইটারেশনের আগে চেক হয় — কন্ডিশন শুরুতেই মিথ্যা হলে বডি একবারও এক্সিকিউট হয় না (শূন্যবার সম্ভব)।
কন্ডিশন প্রতি ইটারেশনের পরে চেক হয় — বডি প্রথমবার আগেই এক্সিকিউট হয়ে যায়, তাই কন্ডিশন যাই হোক না কেন বডি অন্তত একবার চলবেই।
একটি এক্সপ্লিসিট কাউন্টার/রেঞ্জ নিয়ে চলা একটি সুবিধাজনক সংক্ষিপ্ত রূপ — নিচে দেখব এটি আসলে একটি pretest while-লুপেরই বিশেষ সিনট্যাক্স।
৩ · for, while, do-while — একই মেকানিজমের ভিন্ন সিনট্যাক্স
for, while, এবং do-while — এই তিনটি মৌলিকভাবে ভিন্ন কোনো
কম্পিউটেশনাল ক্ষমতা নয়, একই অন্তর্নিহিত লুপিং মেকানিজমের ভিন্ন ভিন্ন সিনট্যাক্টিক সুবিধা
(syntactic convenience) মাত্র। একটি counter-controlled for লুপ — যেমন
for i in range(start, end) — সবসময় একটি সমতুল্য while লুপে যান্ত্রিকভাবে
রিরাইট করা যায়: একটি এক্সপ্লিসিট কাউন্টার ভ্যারিয়েবল ঘোষণা করে, একটি while-কন্ডিশন লিখে, এবং প্রতি
ইটারেশনে ম্যানুয়ালি কাউন্টার বাড়িয়ে। নিচের কোড সেলে এই ইকুইভ্যালেন্স হাতে-কলমে প্রমাণ করা হয়েছে —
Python-এর নেটিভ range()-ভিত্তিক যোগফলের সাথে তুলনা করে।
৪ · কোড: for_to_while কনভার্সন ও pretest/posttest ডেমো
নিচের কোড সেলে দুটো জিনিস দেখানো হচ্ছে — প্রথমত, for_to_while ফাংশনটি শুধুমাত্র
while-লুপ মেশিনারি (এক্সপ্লিসিট কাউন্টার, while-কন্ডিশন, ম্যানুয়াল ইনক্রিমেন্ট) ব্যবহার করে
1 থেকে N পর্যন্ত যোগফল বের করে, এবং সেটাকে Python-এর নেটিভ range()-ভিত্তিক যোগফলের সাথে
মিলিয়ে দেখা হয়। দ্বিতীয়ত, একই initially-false কন্ডিশনে pretest ও posttest লুপের আচরণ আলাদাভাবে
পরীক্ষা করা হয়েছে।
# সিলেকশন: if/else বনাম মাল্টি-ওয়ে সিলেকশনের একই কাজ
def classify_grade(score):
if score >= 90:
return "A+"
elif score >= 80:
return "A"
elif score >= 70:
return "B"
else:
return "F"
for s in [95, 82, 71, 40]:
print(f"স্কোর {s} -> গ্রেড {classify_grade(s)}")
print()
# ইটারেশন: for_to_while -- শুধুমাত্র while-লুপ মেশিনারি দিয়ে counter-controlled লুপ বানানো
def for_to_while(start, end, body_fn):
total = 0
i = start # এক্সপ্লিসিট কাউন্টার ভ্যারিয়েবল
while i <= end: # while-কন্ডিশন (pretest)
total = body_fn(total, i)
i += 1 # ম্যানুয়াল ইনক্রিমেন্ট
return total
def add_step(acc, i):
return acc + i
N = 10
while_sum = for_to_while(1, N, add_step)
range_sum = sum(range(1, N + 1)) # Python-এর নেটিভ for i in range(...) সমতুল্য
print(f"for_to_while(1, {N}) দিয়ে যোগফল: {while_sum}")
print(f"native range(1, {N + 1}) দিয়ে যোগফল: {range_sum}")
print(f"দুটো মিলছে কি না: {while_sum == range_sum}")
print()
# pretest বনাম posttest -- initially-false কন্ডিশনে আচরণের পার্থক্য
def pretest_loop(condition_fn, body_fn):
log = []
while condition_fn(): # কন্ডিশন বডির আগে চেক হয়
log.append(body_fn())
return log
def posttest_loop(condition_fn, body_fn):
log = []
while True:
log.append(body_fn()) # বডি আগে চলে, কন্ডিশন পরে চেক হয়
if not condition_fn():
break
return log
always_false = lambda: False
counter = {"n": 0}
def bump():
counter["n"] += 1
return counter["n"]
pre_log = pretest_loop(always_false, bump)
print(f"pretest (initially false) লগ: {pre_log} (দৈর্ঘ্য {len(pre_log)})")
counter["n"] = 0
post_log = posttest_loop(always_false, bump)
print(f"posttest (initially false) লগ: {post_log} (দৈর্ঘ্য {len(post_log)})")
for_to_while-এর ভেতরে কোথাও Python-এর নিজস্ব for কীওয়ার্ড নেই,
শুধু while, একটি কাউন্টার, আর ম্যানুয়াল ইনক্রিমেন্ট — তবুও এটি range()-এর সাথে
হুবহু একই যোগফল দেয়। আর pretest_loop/posttest_loop-এর আউটপুটে লক্ষ্য করুন —
always_false কন্ডিশন দিয়েও posttest লগে ঠিক একটি এন্ট্রি আছে, কিন্তু pretest লগ সম্পূর্ণ খালি।
এটাই pretest বনাম posttest-এর "শূন্যবার বনাম অন্তত একবার" পার্থক্যের সরাসরি প্রমাণ।
সিলেকশন ও ইটারেশন — এই দুটোই কন্ট্রোল ফ্লোকে সরল রৈখিক (sequential) এক্সিকিউশনের বাইরে নিয়ে যাওয়ার
মৌলিক উপায়। ইটারেশনের ভেতরে for/while/do-while ভিন্ন সিনট্যাক্স
হলেও একই অন্তর্নিহিত মেকানিজম, শুধু pretest বনাম posttest-এর "কন্ডিশন কখন চেক হয়" প্রশ্নটাই আসল
আচরণগত পার্থক্য তৈরি করে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ কেন posttest লুপ সবসময় অন্তত একবার বডি এক্সিকিউট করে, কিন্তু pretest নাও করতে পারে?
এটা সম্পূর্ণভাবে শর্ত-পরীক্ষার ক্রম-এর উপর নির্ভর করে। pretest লুপে (while) কন্ডিশন বডি
এক্সিকিউট হওয়ার আগে চেক হয় — তাই কন্ডিশন প্রথমবারই মিথ্যা হলে বডি একবারও চলার সুযোগ
পায় না (শূন্যবার)। posttest লুপে (do-while) কোডের গঠনটাই উল্টো — বডি আগে এক্সিকিউট হয়ে যায়, কন্ডিশন
পরীক্ষা হয় তারপর, শুধু "আরেকবার চালাব কি না" সিদ্ধান্ত নিতে — অর্থাৎ কন্ডিশন যাই হোক, বডি ইতিমধ্যে
অন্তত একবার চলে গেছে। উপরের কোড সেলে always_false কন্ডিশন দিয়েই এটা সরাসরি দেখা গেছে —
pretest লগ খালি, posttest লগে একটি এন্ট্রি।
প্র ০২ for-লুপকে while দিয়ে সবসময় রিরাইট করা গেলে, তাহলে আলাদা করে for-লুপের প্রয়োজন কী?
কম্পিউটেশনাল ক্ষমতার দিক থেকে কোনো প্রয়োজন নেই — কিন্তু M1/L04-এর ভাষা-ডিজাইন গোলগুলোর কথা মনে করুন:
writability ও reliability। for i in range(1, N+1)
লিখলে কাউন্টার ইনিশিয়ালাইজেশন, কন্ডিশন, ও ইনক্রিমেন্ট — তিনটাই একটি লাইনে, স্পষ্টভাবে দেখা যায়। সমতুল্য
while সংস্করণে এই তিনটা কাজ আলাদা আলাদা লাইনে ম্যানুয়ালি লিখতে হয় — এবং ইনক্রিমেন্ট লাইন
ভুলে বাদ পড়ে গেলে (একটি সাধারণ বাস্তব বাগ) ইনফাইনাইট লুপ তৈরি হতে পারে। তাই for একটি নিরাপদ,
কম error-prone সিনট্যাক্টিক সুবিধা — যদিও ভেতরে ভেতরে একই মেকানিজম চলছে।
প্র ০৩ multi-way selection (switch/match) থাকা সত্ত্বেও কেন প্রায় সব ভাষায় if/else-ও থাকে?
কারণ দুটো ভিন্ন ধরনের সিদ্ধান্তের জন্য ডিজাইন করা। switch/match সবচেয়ে
স্বাভাবিক যখন একটি একক ভ্যারিয়েবলের নির্দিষ্ট, ডিসক্রিট মানগুলোর সাথে মেলানো হচ্ছে (যেমন একটি
এনামের ৫টি সম্ভাব্য মান)। কিন্তু if/else লাগে যখন কন্ডিশন সাধারণ বুলিয়ান এক্সপ্রেশন —
যেমন score >= 90 (একটি রেঞ্জ চেক, নির্দিষ্ট ডিসক্রিট মান নয়) বা একাধিক ভ্যারিয়েবল জড়িত
কোনো জটিল কন্ডিশন। তাই switch/match if/else-কে প্রতিস্থাপন
করে না — শুধু একটি নির্দিষ্ট, সাধারণ প্যাটার্নকে (অনেক ডিসক্রিট কেস) আরও পরিষ্কারভাবে প্রকাশ করার
বিকল্প দেয়।
অনুশীলন
-
চিন্তা করুন:
for_to_whileফাংশনে একটিstepপ্যারামিটার যোগ করুন (ডিফল্ট ১) যাতে এটিrange(start, end + 1, step)-এর সমতুল্য হয়।step=2দিয়ে 1 থেকে 10 পর্যন্ত শুধু বিজোড় সংখ্যাগুলো যোগ করে দেখুন Python-এর নেটিভrange(1, 11, 2)যোগফলের সাথে মেলে কি না।পরিবর্তন হবে
i += step(ম্যানুয়াল ইনক্রিমেন্ট লাইনে1-এর বদলেstep)।step=2, রেঞ্জ 1-10 দিয়ে বিজোড় সংখ্যা 1, 3, 5, 7, 9 যোগ হয় = 25 — যাsum(range(1, 11, 2))-এর ফলাফলের সাথে হুবহু মেলে। এটাই দেখায় counter-controlled লুপের "স্টেপ সাইজ" ধারণাটাও while-মেশিনারির উপরে একটি সাধারণ, প্যারামিটারাইজড অ্যাডঅন — মৌলিক কোনো নতুন ক্ষমতা নয়। -
পরীক্ষা করুন: উপরের কোড সেলে
N-এর মান25করে Run চেপে দেখুনfor_to_whileওrange-ভিত্তিক যোগফল কী দেয়, এবং হাতে-কলমে closed-form সূত্র N(N+1)/2 দিয়ে মিলিয়ে দেখুন।N = 25দিয়ে উভয় পদ্ধতিতেই যোগফল হবে ৩২৫ — এবং সূত্র অনুযায়ী 25 × 26 / 2 = 650 / 2 = 325, যা হুবহু মেলে। এই তিনটে (হাতে-লেখা while-ভিত্তিক লুপ, Python-এর নেটিভ range, ও গাণিতিক closed-form সূত্র) — সবগুলো একই ফলাফল দেওয়াটাই ইটারেশনের সঠিকতার একটি শক্তিশালী ক্রস-চেক।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ পরবর্তী পাঠ — এক্সেপশন হ্যান্ডলিং মেকানিজম — কন্ট্রোল ফ্লোকে আরও একটি দিক থেকে দেখাবে।
- Computer Architecture & Digital Logic কোর্স সহোদর কোর্স উচ্চস্তরের if/else ও লুপ কম্পাইল হয়ে হার্ডওয়্যার লেভেলে কন্ডিশনাল ব্র্যাঞ্চ ইনস্ট্রাকশনে পরিণত হয় — সেই কোর্সে সেই ব্র্যাঞ্চিং মেকানিজম শেখানো হয়েছে।
- Data Structures & Algorithms কোর্স সঙ্গী কোর্স প্রতিটি ইটারেটিভ অ্যালগরিদম (সর্টিং, সার্চিং) আসলে এই পাঠের সিলেকশন ও ইটারেশন স্ট্রাকচারের উপরেই তৈরি — সেই কোর্সে এগুলোর প্রয়োগ দেখানো হয়েছে।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture ও Programming Languages & Compiler Design — সব এক জায়গায়।