প্রোগ্রামিংয়ে, ইটারেশন বলতে একটি কোড ব্লক একাধিকবার পুনরাবৃত্তি করার প্রক্রিয়াকে বোঝায়। এটি while লুপ ও until লুপের মতো ধারণা ব্যবহার করে করা যায়। আবার রিকার্সন ব্যবহার করেও এটি করা যায়, যেখানে একটি ফাংশন নিজেকে কল করে।
তবে বেশিরভাগ সময়েই আপনি এলিমেন্টের একটি কালেকশনের উপর ইটারেট করতে চান, যেমন একটি Array, Range বা String।
Crystal-এ Array ও Range ক্লাস দুটোই Enumerable মডিউল অন্তর্ভুক্ত করে, যা এলিমেন্টগুলোর উপর ইটারেট করার জন্য অনেকগুলো মেথড দেয়।
অন্যদিকে String ক্লাসের ক্যারেক্টারগুলোর উপর ইটারেট করার জন্য নিজস্ব মেথড রয়েছে।
অন্য ভাষাগুলোর মতো Crystal-এও কোনো for স্টেটমেন্ট নেই। বদলে এতে ইটারেট করার জন্য ব্যবহারযোগ্য বেশ কিছু মেথড আছে।
কালেকশনের উপর ইটারেট করার সবচেয়ে কমন উপায় হলো each মেথড ব্যবহার করা, যা কালেকশনের প্রতিটি এলিমেন্ট একটি ব্লকে দেয়।
এটি একটি Range দিয়ে সহজেই করা যায়।
ধরুন আপনি 1 থেকে 3-এর মধ্যে লুপ করতে চান, তাহলে রেঞ্জের উপর ইটারেট করতে each মেথড ব্যবহার করতে পারেন।
(1..3).each do |n|
puts n
end
# Output:
# 1
# 2
# 3
আরও সহজে, আপনি যদি শুধু নির্দিষ্ট সংখ্যকবার ইটারেট করতে চান, তাহলে times মেথড ব্যবহার করতে পারেন, যা Int ক্লাসে রয়েছে।
3.times do |n|
puts n
end
# Output:
# 0
# 1
# 2
String-এর উপর ইটারেশনএকটি String হলো ক্যারেক্টারের একটি ক্রম, আর এটি Enumerable মডিউলের অন্তর্ভুক্ত নয়, অর্থাৎ ক্যারেক্টারগুলোর উপর ইটারেট করার জন্য এর নিজস্ব মেথড রয়েছে।
String-এর উপর ইটারেট করার সবচেয়ে কমন উপায় হলো each_char মেথড ব্যবহার করা, যা String-এর প্রতিটি ক্যারেক্টার একটি ব্লকে দেয়।
each_char মেথড ব্লকে একটি Char অবজেক্ট দেয়, String অবজেক্ট নয়।
str = "hello"
str.each_char { |char| puts char }
# Output:
# h
# e
# l
# l
# o
String-এর উপর ইটারেট করার আরেকটি উপায় হলো each_line মেথড ব্যবহার করা।
এই মেথডটি বেশিরভাগ ক্ষেত্রে একটি ফাইল লাইন ধরে ধরে পড়ার সময় ব্যবহৃত হয়।
str = "hello\nworld"
str.each_line do |line|
puts line
end
# Output:
# hello
# world
Enumerable মডিউল অন্তর্ভুক্ত একটি অবজেক্টের উপর ইটারেশনEnumerable মডিউল একটি কালেকশনের এলিমেন্টগুলোর উপর ইটারেট করার জন্য অনেকগুলো মেথড দেয়।
Enumerable মডিউল অন্তর্ভুক্ত কালেকশনগুলোর মধ্যে রয়েছে Array, Range, Hash, Set এবং আরও কিছু; পরেরগুলো পরের কনসেপ্টগুলোতে আলোচনা করা হবে।
একটি Array-এর উপর ইটারেট করার সবচেয়ে কমন উপায় হলো each মেথড ব্যবহার করা, যা Array-এর প্রতিটি এলিমেন্ট একটি ব্লকে দেয়।
arr = [5, 2, 3]
arr.each do |element|
puts element
end
# Output:
# 5
# 2
# 3
map মেথড একটি Array-এর উপর ইটারেট করার আরেকটি উপায়; এটি প্রতিটি এলিমেন্টে ব্লক প্রয়োগের ফলাফল নিয়ে একটি নতুন Array রিটার্ন করে।
এটি each মেথডের মতোই, তবে এটি রূপান্তরিত এলিমেন্টসহ একটি নতুন Array রিটার্ন করে।
কালেকশনের প্রতিটি এলিমেন্টে একটি ফাংশন প্রয়োগ করতে চাইলে রূপান্তর খুব কাজে লাগে, যা একটি কমন অপারেশন।
arr = [1, 2, 3]
new_arr = arr.map do |element|
element * 2
end
new_arr
# => [2, 4, 6]
if-এর মতো কন্ট্রোল ফ্লো ব্যবহার করে আপনি কোন এলিমেন্টগুলো রূপান্তর করবেন তা ফিল্টার করতে পারেন।
arr = [1, 2, 3]
new_arr = arr.map do |element|
if element.odd?
element * 2
else
element
end
end
new_arr
# => [2, 2, 6]
অনেক সময় আপনি যে এলিমেন্টের উপর ইটারেট করছেন তার ইনডেক্স জানার প্রয়োজন হয়; সেজন্য each_with_index বা map_with_index মেথড ব্যবহার করতে পারেন।
এটি প্রতিটি এলিমেন্ট ও তার ইনডেক্স একটি ব্লকে দেয়, আর মেথডটি শুরুর ইনডেক্স নির্দিষ্ট করার জন্য একটি ঐচ্ছিক আর্গুমেন্টও নেয়।
arr = [1, 2, 3]
arr.each_with_index(4) do |element, index|
puts "Element: #{element}, Index: #{index}"
end
# Output:
# Element: 1, Index: 4
# Element: 2, Index: 5
# Element: 3, Index: 6
মনে রাখবেন, String অবজেক্টের জন্যও each_char_with_index মেথডটি পাওয়া যায়।
sum মেথড কালেকশনের সব এলিমেন্টের যোগফল রিটার্ন করে; এটি যোগ করার আগে এলিমেন্টগুলো রূপান্তর করার জন্য একটি ঐচ্ছিক ব্লকও নেয়।
এটি যোগফলের প্রাথমিক মান নির্দিষ্ট করার জন্য একটি ঐচ্ছিক আর্গুমেন্টও নেয়।
arr = [1, 2, 3]
arr.sum
# => 6
arr.sum(2) { |n| n * 2 }
# => 14
reduce, বা অন্য ভাষায় যা fold নামে পরিচিত, হলো এমন একটি মেথড, যা একটি কম্বাইন মেথড ও একটি প্রাথমিক মান নিয়ে কালেকশনের সব এলিমেন্ট একত্রিত করে।
reduce মেথডে একটি অ্যাকুমুলেটর থাকে যা ব্লকে পাস করা হয়; অ্যাকুমুলেটরটি হলো আগের ইটারেশনের ফলাফল।
এভাবে একটি রিকার্সিভ প্রক্রিয়া তৈরি হয়, যা কালেকশনের সব এলিমেন্ট একত্রিত করে।
arr = [1, 2, 3]
arr.reduce(0) do |acc, n|
acc + n
end
# => 6
reduce হয়তো sum-এর মতো মনে হতে পারে, কিন্তু reduce আরও নমনীয়, কারণ এটি অ্যাকুমুলেটরের প্রাথমিক মান ও কম্বাইন মেথড নির্দিষ্ট করার সুযোগ দেয়।
reduce দিয়ে sum, count এবং অন্যান্য মেথড তৈরি করা যায়।
স্পেলবাউন্ড স্টিল কার্ড গেমে, আপনার কাছে একটি কার্ডের ডেক থাকে যাতে হয় warriors, mages, and rogues-এর মতো বিভিন্ন চরিত্র থাকে, নয়তো fireball, ice storm, and lightning bolt-এর মতো বিভিন্ন স্পেল থাকে। গেমে দুই থেকে চারজন খেলোয়াড় থাকে, এবং লক্ষ্য হলো উচ্চতর পাওয়ার লেভেলের কার্ড খেলে অন্য খেলোয়াড়দের হারানো।
আপনি কার্ডগুলোর পাওয়ার লেভেল গণনা, সাজানো এবং তুলনা করার বিভিন্ন মেথড তৈরি করে গেমে এগিয়ে থাকতে চান।
আপনি নিশ্চিত নন যে একটি কার্ড ডেকে আছে কি না। তাই আপনি এমন একটি মেথড চান যা আপনাকে বলে দেবে আপনি ডেকে যে ধরনের কার্ড খুঁজছেন তার প্রথম কার্ডটি কোথায় পাবেন। অথবা কার্ডটি যদি ডেকে না থাকে, তবে সেটাও আপনি জানতে চান।
একটি মেথড SpellboundSteel.find_card? ইমপ্লিমেন্ট করুন যা String কার্ডের একটি Array এবং খুঁজে বের করার জন্য একটি String কার্ড নেয়।
মেথডটি ডেকে খুঁজে বের করার কার্ডের সাথে মেলে এমন প্রথম কার্ডের ইনডেক্স রিটার্ন করবে।
যদি কার্ডটি ডেকে না থাকে, তবে মেথডটি nil রিটার্ন করবে।
cards = ["Warrior", "Mage", "Rogue", "Fireball", "Ice Storm", "Lightning Bolt", "Mage"]
SpellboundSteel.find_card?(cards, "Mage")
# => 1
কার্ডগুলো সহজে পড়ার জন্য, আপনি কার্ডের নামের প্রথম অক্ষর বড় হাতের করতে চান। এটি এই জন্য যাতে কার্ড খেলার সময় আপনি কার্ডের নামগুলো সহজে দেখতে পারেন।
একটি মেথড SpellboundSteel.capitalize_cards ইমপ্লিমেন্ট করুন যা String কার্ডের একটি Array নেয় এবং কার্ডগুলো রিটার্ন করে যেগুলোর নামের প্রথম অক্ষর বড় হাতের করা হয়েছে।
cards = ["warrior", "mage", "rogue", "fireball", "ice storm", "lightning bolt"]
SpellboundSteel.capitalize_cards(cards)
# => ["Warrior", "Mage", "Rogue", "Fireball", "Ice Storm", "Lightning Bolt"]
গেমে সব কার্ডের পাওয়ার লেভেল গণনা করা গুরুত্বপূর্ণ। সব কার্ডের পাওয়ার নিচের টেবিলে দেখা যাচ্ছে:
| কার্ডের ধরন | পাওয়ার লেভেল |
|---|---|
| Warrior | 10 |
| Mage | 20 |
| Rogue | 30 |
| Fireball | 15 |
| Ice Storm | 25 |
| Lightning Bolt | 35 |
একটি মেথড SpellboundSteel.calculate_power_level ইমপ্লিমেন্ট করুন যা String কার্ডের একটি Array নেয় এবং সব কার্ডের মোট পাওয়ার লেভেল রিটার্ন করে।
cards = ["Warrior", "Mage", "Rogue", "Fireball", "Ice Storm", "Lightning Bolt"]
SpellboundSteel.calculate_power_level(cards)
# => 135
গেমে, তথাকথিত অভিশপ্ত কার্ড রয়েছে। এই কার্ডগুলো একটি লুকানো কার্ড যা শুধুমাত্র যে খেলোয়াড় এটি খেলেছে সে-ই ডিকোড করতে পারে। কার্ডটি ডিকোড করতে, আপনাকে কার্ডের নামের প্রতি দ্বিতীয় অক্ষরটি নিয়ে সেটি সরিয়ে ফেলতে হবে। এতে কার্ডের নামটি পাওয়া যাবে।
একটি মেথড SpellboundSteel.decode_card ইমপ্লিমেন্ট করুন যা একটি String কার্ড নেয় এবং ডিকোড করা কার্ডটি রিটার্ন করে।
SpellboundSteel.decode_card("Wbalrrrlimoqr")
# => "Warrior"
Exercism-এ সাইন আপ করুন, Crystal ট্র্যাকের 26টি কনসেপ্ট142টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।