Erlang-এ নিয়ন্ত্রণ প্রবাহ স্টেটমেন্টস (Control Flow Statements in Erlang)
Erlang একটি ফাংশনাল প্রোগ্রামিং ভাষা হওয়ায় এর নিয়ন্ত্রণ প্রবাহ স্টেটমেন্টগুলি অন্যান্য প্রোগ্রামিং ভাষার মতোই গুরুত্বপূর্ণ, তবে সেগুলি সাধারণত প্যাটার্ন ম্যাচিং এবং গার্ড ক্লজ-এর সাথে ব্যবহার করা হয়। এখানে নিয়ন্ত্রণ প্রবাহের জন্য ব্যবহৃত কিছু মূল স্টেটমেন্ট এবং তাদের ব্যবহার আলোচনা করা হয়েছে।
1. if স্টেটমেন্ট
Erlang-এ if স্টেটমেন্ট শর্তাধীন লজিক পরিচালনা করতে ব্যবহৃত হয়, যেখানে একাধিক শর্তে কার্যক্রম নির্ধারণ করা হয়।
সিনট্যাক্স:
if
Condition1 -> Expression1;
Condition2 -> Expression2;
true -> Expression3
end.- Condition1, Condition2, ইত্যাদি হল শর্ত যা যাচাই করা হয়।
- true হল ডিফল্ট কেস, যদি পূর্বের কোন শর্ত মেলে না।
উদাহরণ:
check_number(N) ->
if
N > 0 -> io:format("Positive~n");
N < 0 -> io:format("Negative~n");
true -> io:format("Zero~n")
end.এখানে, check_number/1 ফাংশনটি একটি পূর্ণসংখ্যা N নেয় এবং এটি শর্তানুসারে আউটপুট প্রদান করে।
2. case স্টেটমেন্ট
case স্টেটমেন্টটি Erlang-এ প্যাটার্ন ম্যাচিংয়ের জন্য ব্যবহৃত হয়, যেখানে বিভিন্ন কেসের মধ্যে একটি নির্দিষ্ট কেস মেলে এবং সেই অনুযায়ী কার্যক্রম চলে।
সিনট্যাক্স:
case Expression of
Pattern1 -> Expression1;
Pattern2 -> Expression2;
_ -> Expression3
end.- Expression: এটি একটি এক্সপ্রেশন যা প্যাটার্ন ম্যাচিং করা হবে।
- Pattern1, Pattern2 ইত্যাদি হল বিভিন্ন প্যাটার্ন।
_হল ডিফল্ট প্যাটার্ন, যেটি কোন কেস মেলেনি এমন পরিস্থিতিতে কার্যকর হয়।
উদাহরণ:
check_case(X) ->
case X of
1 -> io:format("One~n");
2 -> io:format("Two~n");
_ -> io:format("Other~n")
end.এখানে, check_case/1 ফাংশনটি ইনপুট হিসেবে X নেয় এবং তার মানের উপর ভিত্তি করে আউটপুট প্রদান করে।
3. condtional Guards (গার্ড ক্লজ)
Erlang-এ গার্ড ক্লজ (guards) ব্যবহার করা হয় শর্তসমূহের মধ্যে আরও সুনির্দিষ্ট সিদ্ধান্ত নেওয়ার জন্য। এটি when কিওয়ার্ডের মাধ্যমে ব্যবহার করা হয় এবং সাধারণত if এবং case স্টেটমেন্টের সঙ্গে একত্রে ব্যবহৃত হয়।
উদাহরণ:
factorial(N) when N > 0 -> N * factorial(N - 1);
factorial(0) -> 1.এখানে, factorial/1 ফাংশনটি দুটি শর্তে কাজ করে:
- যদি
Nবড় হয় 0 থেকে, তবে এটি রিকার্সিভভাবে factorial গণনা করে। - যদি
Nসমান হয় 0, তবে এটি 1 রিটার্ন করবে।
4. loops and Recursion (পুনরাবৃত্তি এবং লুপ)
Erlang-এ প্রচলিত loops নেই, তবে recursion ব্যবহার করে পুনরাবৃত্তি করা হয়। recursion হচ্ছে এমন একটি প্রক্রিয়া যেখানে একটি ফাংশন নিজেই নিজেকে কল করে।
উদাহরণ:
sum_list([]) -> 0;
sum_list([H | T]) -> H + sum_list(T).এখানে sum_list/1 একটি তালিকার উপাদানগুলির যোগফল বের করতে রিকার্সিভভাবে কাজ করছে:
- প্রথমে যদি তালিকা খালি হয় (base case), তাহলে এটি
0ফেরত দেয়। - না হলে, প্রথম উপাদানটি (
H) যোগ করে বাকি তালিকা (T) এর জন্য আবারsum_list/1ফাংশনটি কল করা হয়।
5. while Loop এর মতো আচরণ
Erlang-এ কোনো সরাসরি while লুপ নেই, তবে আমরা recursion এবং conditional guards ব্যবহার করে এর মতো আচরণ তৈরি করতে পারি।
উদাহরণ:
countdown(0) -> io:format("Done~n");
countdown(N) when N > 0 ->
io:format("Counting down: ~p~n", [N]),
countdown(N - 1).এখানে, countdown/1 ফাংশনটি একটি পূর্ণসংখ্যা N থেকে শুরু করে 0 পর্যন্ত গণনা করে। এটি রিকার্সিভভাবে কাজ করে এবং when গার্ড ক্লজ দিয়ে নিশ্চিত করে যে N 0 এর চেয়ে বড় হলে লুপ চালানো হবে।
6. try-catch (ব্যতিক্রম হ্যান্ডলিং)
Erlang-এ try-catch স্টেটমেন্ট ব্যবহার করা হয় ব্যতিক্রম (exceptions) হ্যান্ডলিংয়ের জন্য। এটি কোনও ত্রুটি ঘটলে সেই ত্রুটিটি হ্যান্ডেল করতে ব্যবহৃত হয়।
সিনট্যাক্স:
try Expression of
Pattern -> Result
catch
Exception -> Error
end.উদাহরণ:
safe_divide(X, Y) ->
try X / Y of
Result -> Result
catch
error:badarith -> "Division by zero error"
end.এখানে, safe_divide/2 ফাংশনটি প্রথমে দুটি সংখ্যার ভাগফল বের করার চেষ্টা করে, এবং যদি বিভাজক (divisor) 0 হয়, তবে এটি একটি ত্রুটি (error:badarith) ধরবে এবং "Division by zero error" রিটার্ন করবে।
উপসংহার
Erlang-এ নিয়ন্ত্রণ প্রবাহ স্টেটমেন্টগুলি যেমন if, case, guards, recursion, এবং try-catch খুবই গুরুত্বপূর্ণ। Erlang এর ফাংশনাল এবং রিকার্সিভ প্রকৃতি তাকে অন্যান্য প্রোগ্রামিং ভাষার তুলনায় কিছুটা আলাদা করে তোলে। এগুলি সঠিকভাবে ব্যবহার করে আপনি জটিল লজিক এবং ব্যতিক্রম হ্যান্ডলিং সহজে করতে পারবেন।
Erlang এ if, case, এবং cond স্টেটমেন্ট
Erlang-এ বিভিন্ন ধরণের কন্ডিশনাল স্টেটমেন্ট ব্যবহৃত হয়, যেমন if, case, এবং cond। এই স্টেটমেন্টগুলি প্রোগ্রামে শর্ত অনুযায়ী বিভিন্ন কার্যক্রম সম্পাদন করার জন্য ব্যবহৃত হয়। এগুলি ফাংশনাল প্রোগ্রামিং ধারণার সাথে সঙ্গতিপূর্ণ এবং কোডের কার্যকারিতা ও পাঠযোগ্যতা বাড়াতে সাহায্য করে।
1. if স্টেটমেন্ট
Erlang-এ if স্টেটমেন্ট একটি শর্তপূর্ণ সিদ্ধান্ত নেওয়ার জন্য ব্যবহৃত হয়। if ব্লকে বিভিন্ন শর্ত থাকতে পারে এবং প্রতিটি শর্তের জন্য একটি এক্সপ্রেশন বা কার্যক্রম থাকে।
সিনট্যাক্স:
if
Condition1 -> Expression1;
Condition2 -> Expression2;
true -> Expression3
end.- Condition1, Condition2: এখানে আপনি যে শর্তটি পরীক্ষা করতে চান তা লিখবেন। শর্ত সত্য হলে সংশ্লিষ্ট Expression কার্যকর হবে।
- true -> Expression3: এটি একটি ডিফল্ট কন্ডিশন, যা সব শর্ত মিথ্যা হলে কার্যকর হয়।
উদাহরণ:
check_number(X) ->
if
X > 0 -> io:format("Positive number~n");
X < 0 -> io:format("Negative number~n");
true -> io:format("Zero~n")
end.এখানে:
- যদি
Xএর মান পজিটিভ হয়, তাহলে "Positive number" প্রিন্ট হবে। - যদি
Xএর মান নেগেটিভ হয়, তাহলে "Negative number" প্রিন্ট হবে। - অন্যথায়, Zero প্রিন্ট হবে।
2. case স্টেটমেন্ট
case স্টেটমেন্টটি একটি নির্দিষ্ট ভেরিয়েবলের মানের উপর ভিত্তি করে একাধিক শর্ত পরীক্ষা করে এবং প্রতিটি শর্ত অনুযায়ী একটি এক্সপ্রেশন বা কার্যক্রম কার্যকর করে। এটি অনেক সময় প্যাটার্ন ম্যাচিং করার জন্য ব্যবহৃত হয়।
সিনট্যাক্স:
case Expression of
Pattern1 -> Expression1;
Pattern2 -> Expression2;
_ -> DefaultExpression
end.- Expression: এটি যে এক্সপ্রেশনটির উপর ভিত্তি করে শর্ত পরীক্ষা হবে।
- Pattern1, Pattern2: প্যাটার্ন ম্যাচিং এবং শর্ত যাচাই করা হয়। যদি কোন প্যাটার্ন মেলে, তবে সংশ্লিষ্ট Expression কার্যকর হয়।
_: এটি একটি ডিফল্ট প্যাটার্ন, যা অন্য সব প্যাটার্ন না মেললে কার্যকর হয়।
উদাহরণ:
check_day(Day) ->
case Day of
monday -> io:format("Start of the week~n");
friday -> io:format("Almost weekend~n");
saturday -> io:format("Weekend!~n");
sunday -> io:format("Rest day~n");
_ -> io:format("Invalid day~n")
end.এখানে:
Dayএর মান অনুসারে সঠিক বার্তা প্রিন্ট হবে।_ব্যবহার করা হয়েছে যাতে কোনো অন্য মান থাকলে "Invalid day" প্রিন্ট হয়।
3. cond স্টেটমেন্ট
Erlang-এর স্ট্যান্ডার্ড লাইব্রেরিতে cond নামে কোনো স্টেটমেন্ট নেই। তবে আপনি সম্ভবত cond হিসেবে অভিপ্রেত case বা if স্টেটমেন্টের মতো কিছু বুঝাতে চাইছেন। যদি cond আপনার নিজস্ব কোনো স্টেটমেন্ট হয়ে থাকে, তবে সাধারণত আপনি case অথবা if ব্যবহার করবেন, যেগুলি যথাযথ কন্ডিশনাল স্টেটমেন্ট হিসেবে কাজ করবে।
তবে, Erlang এ সাধারণত case বা if এর মতো শর্তমূলক স্টেটমেন্ট ব্যবহার করা হয় এবং এগুলি অধিকাংশ পরিস্থিতিতে যথেষ্ট কার্যকরী।
উপসংহার
- if স্টেটমেন্ট: সাধারণ শর্ত পূরণের ভিত্তিতে একাধিক শাখার মধ্যে সিদ্ধান্ত নেয়।
- case স্টেটমেন্ট: প্যাটার্ন ম্যাচিংয়ের মাধ্যমে একাধিক শর্ত পরীক্ষার জন্য ব্যবহৃত হয়।
- cond: Erlang-এ কোনো
condস্টেটমেন্ট নেই, তবেcaseবাifএর মতো স্টেটমেন্টগুলিই কন্ডিশনাল কাজের জন্য ব্যবহৃত হয়।
Pattern Matching এর মাধ্যমে Decision Making
Pattern Matching হল Erlang-এর একটি শক্তিশালী বৈশিষ্ট্য যা আপনাকে ভ্যালু, ডেটা টাইপ এবং স্ট্রাকচার অনুসারে সিদ্ধান্ত নিতে সাহায্য করে। এটি কার্যকরভাবে ডেটা বা ইনপুটের ওপর ভিত্তি করে বিভিন্ন কাজ সম্পাদন করতে ব্যবহৃত হয়। Erlang-এ pattern matching ব্যবহার করা হয় সিদ্ধান্ত নেওয়ার জন্য, যেমন কোন ভ্যালু বা ডেটা টাইপ পাওয়ার ভিত্তিতে ফাংশনের আচরণ পরিবর্তন করা।
Erlang-এ pattern matching ফাংশন ডিফাইনেশনের অংশ হিসেবে কাজ করে এবং ফাংশনের আর্গুমেন্টগুলোর সাথে মিলিয়ে নির্ধারণ করা হয় যে কোন কোড এক্সিকিউট হবে।
Pattern Matching এর মূল ধারণা
Pattern matching এর মাধ্যমে একটি ডেটা স্ট্রাকচারের প্রতিটি উপাদান বা অংশের সাথে মেলানো হয় এবং যদি মেলানো যায়, তবে সেই কোড ব্লক এক্সিকিউট হয়। এটা guard clauses বা conditional statements এর তুলনায় আরও বেশি কার্যকরী এবং সুসংগঠিত, কারণ এটি বিভিন্ন ধরনের পরিস্থিতি এবং ইনপুটের ভিত্তিতে কোডের বিভিন্ন অংশ কার্যকর করে।
Pattern Matching এর উদাহরণ
ধরা যাক, আমাদের একটি ফাংশন রয়েছে যা দুইটি ইনপুটের ওপর ভিত্তি করে কাজ করবে। আমাদের উদ্দেশ্য হল ইনপুট দুটি মিলিয়ে দেখতে এবং নির্দিষ্ট কিছু কাজ সম্পাদন করা।
উদাহরণ ১: একটি সাধারণ Pattern Matching
check_value(0) -> "Zero";
check_value(1) -> "One";
check_value(_) -> "Other".এখানে, check_value/1 ফাংশনে তিনটি ভিন্ন প্যাটার্ন রয়েছে:
- প্রথম প্যাটার্নটি যদি ইনপুট
0হয় তবে"Zero"রিটার্ন করবে। - দ্বিতীয় প্যাটার্নটি যদি ইনপুট
1হয় তবে"One"রিটার্ন করবে। - তৃতীয় প্যাটার্নটি অন্য যেকোনো ভ্যালুর জন্য
"Other"রিটার্ন করবে, কারণ এখানে_ব্যবহৃত হয়েছে, যা একটি wildcard প্যাটার্ন (যা সব ধরনের ইনপুটের সাথে ম্যাচ করে)।
ব্যবহার:
1> check_value(0).
"Zero"
2> check_value(1).
"One"
3> check_value(42).
"Other"এখানে, Erlang নিজে থেকেই ইনপুট অনুযায়ী প্যাটার্ন মেলানোর মাধ্যমে সিদ্ধান্ত নিয়েছে কোন কোড এক্সিকিউট হবে।
Pattern Matching with Tuples
Tuple-এর মাধ্যমে মেলানোও একটি সাধারণ কৌশল। ধরুন, আমরা একটি ফাংশন তৈরি করেছি যা দুটি সংখ্যা নিয়ে গাণিতিক হিসাব করবে। যদি সংখ্যা দুটি একটি tuple হিসেবে আসে, তবে ফাংশনটি সেই টিউপলের প্রথম দুটি মানের ওপর নির্ভর করে সিদ্ধান্ত নেবে।
উদাহরণ ২: Tuple ব্যবহার করে Pattern Matching
sum({a, X, Y}) -> X + Y;
sum({b, X, Y}) -> X * Y;
sum({_, X, Y}) -> X - Y.এখানে, আমরা একটি tuple {a, X, Y} বা {b, X, Y} আর্গুমেন্ট হিসেবে নিচ্ছি এবং সেই অনুযায়ী একটি গাণিতিক কাজ (যোগ, গুণ, অথবা বিয়োগ) করছি।
ব্যবহার:
1> sum({a, 5, 3}).
8
2> sum({b, 5, 3}).
15
3> sum({c, 5, 3}).
2এখানে, {a, 5, 3} প্যাটার্নের সাথে মিলে গেলে যোগফল হবে 8, {b, 5, 3} প্যাটার্নের সাথে মিলে গেলে গুণফল হবে 15, এবং অন্য কোনো প্যাটার্নের সাথে মিলে গেলে বিয়োগফল হবে 2।
Pattern Matching with Lists
Erlang-এ List এর উপাদানগুলোর সাথে মেলানো খুবই সাধারণ এবং গুরুত্বপূর্ণ। ফাংশন ডিফাইন করার সময় আপনি লিস্টের প্রথম উপাদান বা লিস্টের আকারের ভিত্তিতে সিদ্ধান্ত নিতে পারেন।
উদাহরণ ৩: List ব্যবহার করে Pattern Matching
process_list([H | T]) -> "Head: " ++ integer_to_list(H) ++ ", Tail: " ++ lists:concat(map(fun integer_to_list/1, T));
process_list([]) -> "Empty list".এখানে, প্রথম প্যাটার্ন [H | T] একটি লিস্টের প্রথম উপাদান H (হেড) এবং বাকি উপাদানগুলিকে T (টেইল) হিসেবে আলাদা করে নেয়। দ্বিতীয় প্যাটার্নটি খালি লিস্টের জন্য একটি আলাদা সিদ্ধান্ত নেয়।
ব্যবহার:
1> process_list([1, 2, 3, 4]).
"Head: 1, Tail: 234"
2> process_list([]).
"Empty list"এখানে, [1, 2, 3, 4] লিস্টে 1 হল হেড এবং [2, 3, 4] হল টেইল। অন্যদিকে খালি লিস্টের জন্য "Empty list" রিটার্ন হয়।
Pattern Matching with Guards
Guards ব্যবহারের মাধ্যমে আপনি pattern matching এর সাথে কিছু শর্তও যোগ করতে পারেন। Guards হল অতিরিক্ত শর্ত যা when ক্লজের মাধ্যমে ব্যবহার করা হয়, যা প্যাটার্ন ম্যাচের পর ফাংশনটি যখন কিছু অতিরিক্ত শর্ত পূরণ করে তখন কার্যকর হয়।
উদাহরণ ৪: Pattern Matching with Guards
check_number(N) when N >= 0 -> "Positive or Zero";
check_number(N) when N < 0 -> "Negative".এখানে, যদি সংখ্যা ০ বা তার চেয়ে বড় হয় তবে "Positive or Zero" রিটার্ন হবে, এবং যদি সংখ্যা ঋণাত্মক হয় তবে "Negative" রিটার্ন হবে।
ব্যবহার:
1> check_number(5).
"Positive or Zero"
2> check_number(-3).
"Negative"এখানে, গার্ড ব্যবহারের মাধ্যমে একটি শর্ত যুক্ত করা হয়েছে যেটি প্যাটার্ন ম্যাচের পরে সিদ্ধান্ত নিয়েছে যে ইনপুটটি ০ বা তার চেয়ে বড় কি না।
উপসংহার
Pattern Matching Erlang এর একটি শক্তিশালী বৈশিষ্ট্য, যা decision making এর জন্য খুবই উপকারী। এটি কোডকে অনেক বেশি পরিষ্কার এবং দক্ষ করে তোলে কারণ এটি ডেটার সাথে মিলিয়ে সিদ্ধান্ত নেয়। Pattern matching এর মাধ্যমে আপনি বিভিন্ন ডেটা টাইপের জন্য নির্দিষ্ট কোড এক্সিকিউট করতে পারেন, যেমন atom, tuple, list, এবং guards ব্যবহার করে আরও সূক্ষ্ম শর্ত তৈরি করতে পারেন।
Erlang-এ Functions এবং Recursion এর ব্যবহার
Erlang একটি ফাংশনাল প্রোগ্রামিং ভাষা, যেখানে functions (ফাংশন) এবং recursion (রিকার্সন) ব্যবহৃত হয়। Erlang-এ ফাংশন খুবই গুরুত্বপূর্ণ এবং এটি প্রোগ্রামিংয়ের মূল অঙ্গ। রিকার্সন Erlang-এ একটি মৌলিক কৌশল, যা পুনরাবৃত্তি (iteration) বা পুনরায় একটি ফাংশন নিজেকে কল করার মাধ্যমে কোনো কাজ সম্পন্ন করার জন্য ব্যবহৃত হয়। এখানে functions এবং recursion এর ব্যবহার সম্পর্কে বিস্তারিত আলোচনা করা হলো।
1. Functions (ফাংশন)
Erlang একটি pure functional programming language, যার মানে এখানে functions ব্যবহার করে প্রোগ্রাম লেখা হয়। Erlang-এ প্রতিটি কার্যক্রম একটি ফাংশনের মধ্যে থাকে এবং ফাংশনগুলিতে pattern matching ও guards ব্যবহার করা যায়।
ফাংশন ডিফিনিশন:
Erlang-এ ফাংশন একটি মডিউলে ডিফাইন করা হয়। প্রতিটি ফাংশন সাধারণত একটি head এবং body নিয়ে গঠিত, যেখানে head হল ফাংশনের নাম এবং প্যারামিটার, আর body হল ফাংশনের কার্যকারিতা।
উদাহরণ:
-module(math).
-export([add/2, multiply/2]).
add(X, Y) ->
X + Y.
multiply(X, Y) ->
X * Y.এখানে, add/2 এবং multiply/2 দুটি ফাংশন রয়েছে যা দুটি সংখ্যার যোগফল এবং গুণফল প্রদান করবে। ফাংশনগুলির সিগনেচার হচ্ছে add/2 এবং multiply/2, যার মানে হলো প্রতিটি ফাংশন দুটি প্যারামিটার নেবে।
2. Function Clauses and Pattern Matching (ফাংশন ক্লজ এবং প্যাটার্ন মাচিং)
Erlang-এ ফাংশন একাধিক ক্লজ বা শর্ত নিয়ে কাজ করতে পারে। Pattern matching এর মাধ্যমে ফাংশন সঠিকভাবে কল করা হয় এবং কার্যকরী ফলাফল পাওয়া যায়। একাধিক ক্লজ ব্যবহার করার মাধ্যমে আমরা বিভিন্ন ইনপুট অনুযায়ী আলাদা কাজ করতে পারি।
উদাহরণ:
-module(calculator).
-export([compute/1]).
compute({add, X, Y}) ->
X + Y;
compute({subtract, X, Y}) ->
X - Y;
compute({multiply, X, Y}) ->
X * Y;
compute({divide, X, Y}) when Y /= 0 ->
X / Y;
compute({divide, _, 0}) ->
{error, "Division by zero!"}.এখানে, compute/1 ফাংশনটি একাধিক ক্লজ নিয়ে কাজ করছে, যেখানে প্রতিটি ক্লজ এক একটি নির্দিষ্ট অপারেশন সম্পাদন করে (যেমন যোগফল, বিয়োগফল, গুণফল, ভাগফল)। এর মধ্যে একটি when গার্ডও ব্যবহার করা হয়েছে, যা divide অপারেশনটি কেবল তখনই কার্যকর করবে যখন বিভাজক শূন্য হবে না।
3. Recursion (রিকার্সন)
Erlang-এ recursion ব্যবহৃত হয় যখন কোনো কাজ সম্পাদন করতে একটি ফাংশন নিজেকে পুনরায় কল করে। এটি মূলত iteration বা পুনরাবৃত্তি কাজের জন্য ব্যবহৃত হয়, কারণ Erlang-এ loops নেই (যেমন সি বা জাভাতে)। রিকার্সন অত্যন্ত গুরুত্বপূর্ণ এবং প্রায়শই ব্যবহৃত একটি কৌশল।
উদাহরণ ১: লিস্টের যোগফল
-module(recursion_example).
-export([sum/1]).
sum([Head | Tail]) ->
Head + sum(Tail);
sum([]) ->
0.এখানে, sum/1 ফাংশনটি একটি লিস্টের উপাদানগুলো যোগ করার জন্য রিকার্সন ব্যবহার করছে:
- প্রথম ক্লজটি লিস্টের প্রথম উপাদান (Head) এবং বাকি উপাদানগুলো (Tail) একসাথে যোগ করে পুনরায়
sum/1ফাংশনকে কল করছে। - দ্বিতীয় ক্লজটি বেস কেস, যেখানে খালি লিস্ট পেলে যোগফল হবে
0।
উদাহরণ ২: ফ্যাক্টোরিয়াল হিসাব করা
-module(factorial).
-export([calculate/1]).
calculate(0) -> 1;
calculate(N) when N > 0 -> N * calculate(N - 1).এখানে, calculate/1 ফাংশনটি রিকার্সন ব্যবহার করে একটি পূর্ণসংখ্যার ফ্যাক্টোরিয়াল বের করার জন্য ব্যবহৃত হয়েছে:
- প্রথম ক্লজটি 0 এর জন্য ফ্যাক্টোরিয়াল 1 রিটার্ন করবে।
- দ্বিতীয় ক্লজটি
Nএর মান বড় হলে নিজেকেN-1দিয়ে কল করবে।
4. Tail Recursion (টেইল রিকার্সন)
Erlang-এ tail recursion এমন একটি রিকার্সন কৌশল, যেখানে রিকার্সন কলটি ফাংশনের শেষে থাকে। এটি আরো দক্ষ এবং stack overflow থেকে রক্ষা পায়, কারণ Erlang এর garbage collector রিকার্সন কলের শেষে হিসাব করে মেমরি ব্যবহারের ওপর নজর রাখে।
উদাহরণ:
-module(tail_recursion).
-export([factorial/1]).
factorial(N) -> factorial(N, 1).
factorial(0, Acc) -> Acc;
factorial(N, Acc) when N > 0 -> factorial(N - 1, N * Acc).এখানে, factorial/1 ফাংশনটি factorial/2 নামক একটি সহায়ক ফাংশন ব্যবহার করে এবং টেইল রিকার্সন ব্যবহৃত হয়েছে:
- প্রথম ক্লজটি
Acc(অ্যাকিউমুলেটর) এর মান রিটার্ন করে, যা ফ্যাক্টোরিয়াল হিসাবের জন্য ব্যবহৃত হয়। - দ্বিতীয় ক্লজটি
Nএর মান কমাতে এবংAccআপডেট করতেfactorial/2ফাংশনটি পুনরায় কল করে।
5. Higher-Order Functions (হায়ার-অর্ডার ফাংশন)
Erlang-এ higher-order functions ব্যবহৃত হয়, যেখানে একটি ফাংশন অন্য একটি ফাংশনকে আর্গুমেন্ট হিসেবে গ্রহণ করতে পারে বা একটি ফাংশন রিটার্ন করতে পারে।
উদাহরণ:
-module(higher_order).
-export([map/2, square/1]).
map([], _) -> [];
map([Head | Tail], Fun) -> [Fun(Head) | map(Tail, Fun)].
square(X) -> X * X.এখানে, map/2 একটি হায়ার-অর্ডার ফাংশন যা একটি ফাংশন (Fun) গ্রহণ করে এবং একটি লিস্টের উপর সেই ফাংশন প্রয়োগ করে। square/1 ফাংশনটি একটি সংখ্যা যেকোনো সংখ্যার বর্গফল দেয়।
উপসংহার
Erlang-এ functions এবং recursion দুটি অত্যন্ত গুরুত্বপূর্ণ কৌশল যা কোডের কার্যকারিতা এবং স্কেলেবিলিটি নিশ্চিত করে। Functions ফাংশনাল প্রোগ্রামিংয়ের মাধ্যে কোডের পুনঃব্যবহারযোগ্যতা এবং পরিষ্কারতা বাড়ায়, আর recursion পুনরাবৃত্তি এবং iteration সম্পর্কিত কাজের জন্য অত্যন্ত কার্যকরী। Erlang-এ রিকার্সন এবং ফাংশনাল ডিজাইন প্যাটার্নগুলি কোডকে আরও সহজ, পরিষ্কার এবং নির্ভরযোগ্য করে তোলে।
Erlang এ receive এবং after ব্লক এর মাধ্যমে Message Handling
Erlang একটি concurrent ভাষা, যেখানে প্রসেসগুলো একে অপরের সাথে message passing এর মাধ্যমে যোগাযোগ করে। receive এবং after ব্লকগুলির মাধ্যমে Erlang-এ message handling (বার্তা পরিচালনা) করা হয়। এই ব্লকগুলি প্রসেসগুলির মধ্যে বার্তা গ্রহণ এবং সময়মতো কাজ করার জন্য ব্যবহৃত হয়।
1. receive ব্লক (Message Reception)
Erlang এ, একটি প্রসেস যখন অন্য প্রসেস থেকে একটি মেসেজ গ্রহণ করতে চায়, তখন receive ব্লক ব্যবহার করা হয়। এটি একটি অ্যাসিঙ্ক্রোনাস অপারেশন, যার মাধ্যমে প্রসেস অন্য প্রসেসের কাছ থেকে বার্তা (message) পেতে পারে। যদি একটি প্রসেসের কাছে কোনও মেসেজ না থাকে, তবে সেই প্রসেস receive ব্লকের মধ্যে অপেক্ষা করবে যতক্ষণ না বার্তা আসে।
receive ব্লকের সিনট্যাক্স:
receive
Pattern1 -> Expression1;
Pattern2 -> Expression2;
...
endএখানে, Pattern1, Pattern2 ইত্যাদি হল মেসেজের প্যাটার্ন যা প্রসেস গ্রহণ করতে পারে। যখন একটি বার্তা প্যাটার্নের সাথে ম্যাচ করবে, তখন সেই মেসেজের সাথে সম্পর্কিত Expression মূল্যায়ন হবে। একাধিক প্যাটার্ন থাকতে পারে, এবং Erlang সেগুলির মধ্যে মেলানো প্রথম প্যাটার্নটি নির্বাচন করবে।
উদাহরণ:
-module(message_handler).
-export([start/0, receive_message/0]).
start() ->
spawn(message_handler, receive_message, []).
receive_message() ->
receive
{hello, Msg} -> io:format("Received hello: ~s~n", [Msg]);
{goodbye, Msg} -> io:format("Received goodbye: ~s~n", [Msg])
end.এখানে, receive_message ফাংশন একটি মেসেজ গ্রহণ করে এবং তার প্যাটার্নের সাথে মেলানো বার্তা অনুযায়ী এক্সপ্রেশনটি কার্যকরী হবে। যদি {hello, Msg} বা {goodbye, Msg} ধরনের বার্তা আসে, তাহলে সেগুলির জন্য আলাদা আউটপুট প্রদান করা হবে।
এটি ব্যবহার করতে:
1> c(message_handler).
{ok,message_handler}
2> Pid = message_handler:start().
<0.37.0>
3> Pid ! {hello, "World"}.
Received hello: "World"এখানে, আমরা Pid প্রাপ্ত প্রসেসে {hello, "World"} মেসেজ পাঠাচ্ছি এবং প্রসেসটি সেই মেসেজ গ্রহণ করে আউটপুট প্রদান করবে।
2. after ব্লক (Timeout Handling)
Erlang এর receive ব্লক একটি সুবিধা প্রদান করে, যেটি হল after ব্লক। এটি একটি টাইমআউট নির্ধারণ করতে সাহায্য করে, যেখানে receive ব্লকটি যদি নির্দিষ্ট সময়ের মধ্যে মেসেজ না পায়, তবে এটি after ব্লকের কোড চালাবে। এটি অপেক্ষা করার সময় একটি সময়সীমা নির্ধারণ করার জন্য ব্যবহার করা হয়।
after ব্লকের সিনট্যাক্স:
receive
Pattern1 -> Expression1;
Pattern2 -> Expression2;
...
after
TimeOut -> TimeoutExpression
endএখানে, TimeOut হল টাইমআউটের সময় (মিলিসেকেন্ডে), এবং যদি মেসেজ পাওয়া না যায়, তবে TimeoutExpression চলে যাবে।
উদাহরণ:
-module(message_handler).
-export([start/0, receive_message/0]).
start() ->
spawn(message_handler, receive_message, []).
receive_message() ->
receive
{hello, Msg} -> io:format("Received hello: ~s~n", [Msg]);
{goodbye, Msg} -> io:format("Received goodbye: ~s~n", [Msg])
after 5000 -> io:format("Timeout occurred, no message received~n")
end.এখানে, receive_message/0 ফাংশনে, একটি টাইমআউটও নির্ধারণ করা হয়েছে। যদি পাঁচ সেকেন্ডের মধ্যে কোনও মেসেজ না আসে, তবে after 5000 ব্লকটি কাজ করবে এবং "Timeout occurred" বার্তা প্রদর্শিত হবে।
এটি ব্যবহার করতে:
1> c(message_handler).
{ok,message_handler}
2> Pid = message_handler:start().
<0.37.0>
3> timer:sleep(6000).
6
Timeout occurred, no message receivedএখানে, আমরা ৬ সেকেন্ডের জন্য অপেক্ষা করছি, যার ফলে টাইমআউট বার্তা প্রদর্শিত হবে কারণ কোনো মেসেজ আসেনি।
3. receive এবং after ব্যবহার করার সুবিধা
- Message Handling: Erlang এর
receiveব্লক প্রসেসের মধ্যে মেসেজ পাঠানো এবং গ্রহণ করার জন্য ব্যবহৃত হয়। এটি প্রসেসগুলির মধ্যে যোগাযোগ প্রতিষ্ঠা করতে সাহায্য করে, যা Erlang এর মূল শক্তি। - Concurrency Management: Erlang এর
receiveএবংafterব্লকগুলি বিভিন্ন প্রসেসের মধ্যে কার্যকরভাবে তথ্য আদান-প্রদান এবং টাইমআউট পরিচালনা করতে সক্ষম। এটি concurrent সিস্টেমের জন্য অপরিহার্য। - Timeout Management:
afterব্লকটি টাইমআউট পরিচালনা করতে সাহায্য করে, যাতে আপনি কোনও নির্দিষ্ট সময়সীমার মধ্যে কাজ করতে পারেন এবং এরপর টাইমআউট প্রসেসটি পরিচালনা করতে পারেন। এটি অনেক ডিস্ট্রিবিউটেড সিস্টেমে গুরুত্বপূর্ণ, যেমন সিস্টেমে কোনো কাজ বা বার্তা গ্রহণ না হলে ফেইলরিওভার বা রিপিট অ্যাটেম্পটস করা।
উপসংহার
Erlang এর receive এবং after ব্লকগুলি বার্তা গ্রহণ এবং টাইমআউট পরিচালনার জন্য ব্যবহৃত হয়। receive ব্লক প্রসেসের মধ্যে মেসেজ গ্রহণ করার জন্য এবং after ব্লকটি টাইমআউট নির্ধারণের জন্য ব্যবহৃত হয়, যা Erlang এর ডিস্ট্রিবিউটেড এবং কনকারেন্ট সিস্টেমে কার্যকরী যোগাযোগ নিশ্চিত করতে সাহায্য করে।
Read more