রুবি ম্যাজিকের আরেকটি সংস্করণে আবার স্বাগতম! এক বছর আগে, আমরা রুবির Enumerable
সম্পর্কে শিখেছি মডিউল, যা অ্যারে, রেঞ্জ এবং হ্যাশের মতো গণনাযোগ্য বস্তুর সাথে কাজ করার সময় আপনি যে পদ্ধতিগুলি ব্যবহার করেন তা প্রদান করে৷
তারপরে, আমরা একটি LinkedList
তৈরি করেছি #each
প্রয়োগ করে কীভাবে একটি বস্তুকে গণনাযোগ্য করা যায় তা দেখানোর জন্য ক্লাস এর উপর পদ্ধতি। Enumerable
অন্তর্ভুক্ত করে মডিউল, আমরা #count
এর মত পদ্ধতি কল করতে সক্ষম হয়েছি , #map
এবং #select
নিজেদেরকে বাস্তবায়ন না করেই যেকোন লিঙ্ক করা তালিকায়।
আমরা শিখেছি কিভাবে গণনাযোগ্য ব্যবহার করতে হয়, কিন্তু তারা কিভাবে কাজ করে? রুবিতে গণনাযোগ্য জাদুর অংশটি তাদের অভ্যন্তরীণ বাস্তবায়ন থেকে আসে, যা পুরোটাই একক #each
এর উপর ভিত্তি করে পদ্ধতি, এবং এমনকি চেইন গণনাকারীদের অনুমতি দেয়।
আজ, আমরা শিখব কিভাবে পদ্ধতিগুলি Enumerable
-এ ক্লাস বাস্তবায়িত হয় এবং কিভাবে Enumerator
অবজেক্ট চেইনিং গণনা পদ্ধতির অনুমতি দেয়।
আপনি যেহেতু অভ্যস্ত হয়ে গেছেন, আমরা Enumerable
এর নিজস্ব সংস্করণগুলি বাস্তবায়ন করে গভীরে ডুব দেব মডিউল এবং Enumerator
ক্লাস সুতরাং, আপনার ওভার-ইঞ্জিনিয়ারিং হেলমেট পরুন এবং চলুন!
লিঙ্ক করা তালিকা
শুরু করার আগে, আসুন আমরা পূর্বে যে লিঙ্কড লিস্ট ক্লাস লিখেছিলাম তার একটি নতুন সংস্করণ দিয়ে শুরু করা যাক।
class LinkedList
def initialize(head = nil, *rest)
@head = head
if rest.first.is_a?(LinkedList)
@tail = rest.first
elsif rest.any?
@tail = LinkedList.new(*rest)
end
end
def <<(head)
@head ? LinkedList.new(head, self) : LinkedList.new(head)
end
def inspect
[@head, @tail].compact
end
def each(&block)
yield @head if @head
@tail.each(&block) if @tail
end
end
পূর্ববর্তী সংস্করণের বিপরীতে, এই বাস্তবায়ন খালি তালিকা তৈরি করতে দেয়, পাশাপাশি দুটির বেশি আইটেম সহ তালিকা তৈরি করতে দেয়। এই সংস্করণটি অন্যটি শুরু করার সময় একটি লিঙ্কযুক্ত তালিকাকে টেল হিসাবে পাস করার অনুমতি দেয়৷
irb> LinkedList.new
=> []
irb> LinkedList.new(1)
=> [1]
irb> LinkedList.new(1, 2)
=> [1,[2]]
irb> LinkedList.new(1, 2, 3)
=> [1,[2,[3]]]
irb> LinkedList.new(1, LinkedList.new(2, 3))
=> [1,[2,[3]]]
irb> LinkedList.new(1, 2, LinkedList.new(3))
=> [1,[2,[3]]]
পূর্বে, আমাদের LinkedLIst
ক্লাসে Enumerable
অন্তর্ভুক্ত মডিউল Enumerable
ব্যবহার করে একটি বস্তুর উপর ম্যাপিং করার সময় এর পদ্ধতিতে, ফলাফল একটি অ্যারেতে সংরক্ষণ করা হয়। এইবার, আমাদের পদ্ধতিগুলি পরিবর্তে নতুন লিঙ্কযুক্ত তালিকাগুলি ফিরিয়ে দেয় তা নিশ্চিত করতে আমরা আমাদের নিজস্ব সংস্করণ প্রয়োগ করব৷
গণনাযোগ্য পদ্ধতি
রুবির Enumerable
মডিউলটি #map
এর মত গণনা পদ্ধতির সাথে আসে , #count
, এবং #select
. #each
বাস্তবায়ন করে পদ্ধতি এবং Enumerable
সহ আমাদের ক্লাসে মডিউল, আমরা আমাদের লিঙ্ক করা তালিকায় সরাসরি সেই পদ্ধতিগুলি ব্যবহার করতে সক্ষম হব৷
পরিবর্তে, আমরা DIYEnumerable
প্রয়োগ করব এবং রুবির সংস্করণের পরিবর্তে এটি আমদানি করুন। এটি এমন কিছু নয় যা আপনি সাধারণত করবেন, তবে এটি আমাদেরকে অভ্যন্তরীণভাবে গণনা কীভাবে কাজ করে সে সম্পর্কে একটি পরিষ্কার অন্তর্দৃষ্টি দেবে৷
চলুন শুরু করা যাক #count
দিয়ে . Enumerable
-এ প্রতিটি আমদানিযোগ্য পদ্ধতি ক্লাস #each
ব্যবহার করে পদ্ধতি আমরা আমাদের LinkedList
এ প্রয়োগ করেছি তাদের ফলাফল গণনা করার জন্য বস্তুর উপর লুপ করার জন্য ক্লাস।
module DIYEnumerable
def count
result = 0
each { |element| result += 1 }
result
end
end
এই উদাহরণে, আমরা #count
প্রয়োগ করেছি একটি নতুন DIYEnumerable
এ পদ্ধতি মডিউল যা আমরা আমাদের লিঙ্কযুক্ত তালিকায় অন্তর্ভুক্ত করব। এটি শূন্যে একটি কাউন্টার শুরু করে এবং #each
কে কল করে প্রতি লুপের জন্য কাউন্টারে একটি যোগ করার পদ্ধতি। সমস্ত উপাদান লুপ করার পরে, পদ্ধতিটি ফলাফল কাউন্টারটি ফেরত দেয়।
module DIYEnumerable
# ...
def map
result = LinkedList.new
each { |element| result = result << yield(element) }
result
end
end
#map
পদ্ধতি একইভাবে প্রয়োগ করা হয়। একটি কাউন্টার রাখার পরিবর্তে, এটি একটি সঞ্চয়কারী ব্যবহার করে, যা একটি খালি তালিকা হিসাবে শুরু হয়। আমরা তালিকার সমস্ত উপাদান লুপ করব এবং প্রতিটি উপাদানে পাস করা ব্লক প্রদান করব। প্রতিটি ফলনের ফলাফল সঞ্চয়ক তালিকায় যুক্ত করা হয়।
ইনপুট তালিকার সমস্ত উপাদান লুপ করার পরে পদ্ধতিটি সঞ্চয়কারীকে ফেরত দেয়।
class LinkedList
include DIYEnumerable
#...
end
DIYEnumerable
অন্তর্ভুক্ত করার পরে আমাদের LinkedLIst
-এ , আমরা আমাদের নতুন যোগ করা #count
পরীক্ষা করতে পারি এবং #map
পদ্ধতি।
irb> list = LinkedList.new(73, 12, 42)
=> [73, [12, [42]]]
irb> list.count
=> 3
irb> list.map { |element| element * 10 }
=> [420, [120, [730]]]
উভয় পদ্ধতিই কাজ করে! #count
পদ্ধতিটি তালিকার আইটেমগুলিকে সঠিকভাবে গণনা করে এবং #map
পদ্ধতি প্রতিটি আইটেমের জন্য একটি ব্লক চালায় এবং একটি আপডেট তালিকা প্রদান করে৷
উল্টানো তালিকা
যাইহোক, #map
পদ্ধতিটি তালিকাটি ফিরিয়ে দিয়েছে বলে মনে হচ্ছে। এটি #<<
হিসাবে বোধগম্য আমাদের লিঙ্কড লিস্ট ক্লাসের মেথড আইটেমগুলিকে যুক্ত করার পরিবর্তে তালিকায় প্রিপেন্ড করে, যা লিঙ্ক করা তালিকার পুনরাবৃত্ত প্রকৃতির একটি বৈশিষ্ট্য।
এমন পরিস্থিতিতে যেখানে তালিকার ক্রম বজায় রাখা অপরিহার্য, সেখানে ম্যাপিং করার সময় আমাদের তালিকাটিকে বিপরীত করার একটি উপায় প্রয়োজন। রুবি Enumerable#reverse_each
প্রয়োগ করে , যা বিপরীত দিকে একটি বস্তুর উপর loops. যা আমাদের সমস্যার একটি চমৎকার সমাধানের মত শোনাচ্ছে। দুঃখের বিষয়, আমরা এর মতো একটি পদ্ধতি ব্যবহার করতে পারি না কারণ আমাদের তালিকাটি নেস্ট করা আছে। আমরা জানি না যে তালিকাটি কত দীর্ঘ হবে যতক্ষণ না আমরা এটি সম্পূর্ণরূপে লুপ করি।
উল্টো তালিকায় ব্লক চালানোর পরিবর্তে, আমরা #reverse_each
-এর একটি সংস্করণ যোগ করব যে এই দুই ধাপ করে. এটি প্রথমে একটি নতুন তালিকা তৈরি করে এটিকে বিপরীত করতে তালিকার উপরে লুপ করে। এর পরে, এটি বিপরীত তালিকার উপর ব্লক চালায়।
module DIYEnumerable
# ...
def reverse_each(&block)
list = LinkedList.new
each { |element| list = list << element }
list.each(&block)
end
def map
result = LinkedList.new
reverse_each { |element| result = result << yield(element) }
result
end
end
এখন, আমরা #reverse_each
ব্যবহার করব আমাদের #map
-এ পদ্ধতি, এটি সঠিক ক্রমে ফিরে এসেছে তা নিশ্চিত করতে।
irb> list = LinkedList.new(73, 12, 42)
=> [73, [12, [42]]]
irb> list.map { |element| element * 10 }
=> [730, [120, [420]]]
এটা কাজ করে! যখনই আমরা আমাদের #map
কল করি একটি লিঙ্ক তালিকায় পদ্ধতি, আমরা মূলের মতো একই ক্রমে একটি নতুন তালিকা ফিরে পাব৷
গণনাকারীদের সাথে চেইনিং গণনা
#each
এর মাধ্যমে আমাদের লিঙ্ক করা তালিকা ক্লাসে প্রয়োগ করা পদ্ধতি এবং অন্তর্ভুক্ত DIYEnumerator
, আমরা এখন উভয় উপায় লুপ করতে পারি এবং লিঙ্ক করা তালিকার উপর ম্যাপ করতে পারি।
irb> list.each { |x| p x }
73
12
42
irb> list.reverse_each { |x| p x }
42
12
73
irb> list.reverse_each.map { |x| x * 10 }
=> [730, [120, [420]]]
=> [420, [120, [730]]]
যাইহোক, যদি আমাদের মানচিত্র করতে হয় তাহলে কি হবে বিপরীত একটি তালিকা উপর? যেহেতু আমরা এখন এটির উপরে ম্যাপ করার আগে তালিকাটিকে বিপরীত করি, তাই এটি সর্বদা মূল তালিকার মতো একই ক্রমে ফিরে আসে। আমরা ইতিমধ্যেই #reverse_each
উভয়ই প্রয়োগ করেছি এবং #map
, তাই আমরা পিছনের দিকে ম্যাপ করতে সক্ষম হতে তাদের একসাথে চেইন করতে সক্ষম হওয়া উচিত। ভাগ্যক্রমে, রুবির Enumerator
ক্লাস এতে সাহায্য করতে পারে।
শেষবার, আমরা Kernel#to_enum
কল করা নিশ্চিত করেছি যদি LinkedList#each
পদ্ধতি একটি ব্লক ছাড়া বলা হয়. এটি একটি Enumerator
ফেরত দিয়ে গণনাযোগ্য পদ্ধতি চেইন করার অনুমতি দেয় বস্তু কিভাবে Enumerator
তা খুঁজে বের করতে ক্লাস কাজ করে, আমরা আমাদের নিজস্ব সংস্করণ বাস্তবায়ন করব।
class DIYEnumerator
include DIYEnumerable
def initialize(object, method)
@object = object
@method = method
end
def each(&block)
@object.send(@method, &block)
end
end
রুবির Enumerator
এর মত , আমাদের গণনাকারী ক্লাস একটি বস্তুর উপর একটি পদ্ধতির চারপাশে একটি মোড়ক। আবৃত বস্তু পর্যন্ত বুদবুদ করে, আমরা গণনা পদ্ধতি চেইন করতে পারি।
এটি কাজ করে কারণ একটি DIYEnumerator
উদাহরণ নিজেই গণনাযোগ্য। এটি #each
প্রয়োগ করে মোড়ানো বস্তুটিকে কল করে, এবং DIYEnumerable
অন্তর্ভুক্ত করে মডিউল যাতে সমস্ত গণনাযোগ্য পদ্ধতি এটিতে কল করা যায়।
আমরা আমাদের DIYEnumerator
এর একটি উদাহরণ ফেরত দেব LinkedList#each
এ কোন ব্লক পাস না হলে ক্লাস পদ্ধতি।
class LinkedList
# ...
def each(&block)
if block_given?
yield @head
@tail.each(&block) if @tail
else
DIYEnumerator.new(self, :each)
end
end
end
আমাদের নিজস্ব গণনাকারী ব্যবহার করে, আমরা এখন #reverse_each
-এ একটি খালি ব্লক পাস না করেই মূল ক্রমে ফলাফল পেতে চেইন গণনা করতে পারি। পদ্ধতি কল।
irb> list = LinkedList.new(73, 12, 42)
=> [73, [12, [42]]]
irb> list.map { |element| element * 10 }
=> [420, [120, [730]]]
আগ্রহী এবং অলস গণনা
এটি Enumerable
বাস্তবায়নে আমাদের উঁকিঝুঁকি শেষ করে মডিউল এবং Enumerator
আপাতত ক্লাস। আমরা শিখেছি কীভাবে কিছু গণনাযোগ্য পদ্ধতি কাজ করে এবং কীভাবে একজন গণনাকারী একটি গণনাযোগ্য বস্তুকে মোড়ানোর মাধ্যমে গণনাকে চেইন করতে সাহায্য করে।
যদিও আমাদের পদ্ধতির সাথে কিছু সমস্যা রয়েছে। এর প্রকৃতি অনুসারে, গণনা আগ্রহী , যার অর্থ এটিতে গণনাযোগ্য পদ্ধতিগুলির মধ্যে একটি কল করার সাথে সাথে এটি তালিকার উপর দিয়ে লুপ করে। যদিও এটি বেশিরভাগ ক্ষেত্রেই ভাল, তবে একটি তালিকার উপরে ম্যাপিং বিপরীতে তালিকাটিকে দুবার বিপরীত করে দেয়, যা অপ্রয়োজনীয় হওয়া উচিত।
লুপের সংখ্যা কমাতে, আমরা Enumerator::Lazy
নিয়োগ করতে পারি শেষ মুহূর্ত পর্যন্ত লুপ করতে দেরি করতে, এবং ডুপ্লিকেট তালিকার বিপরীতে নিজেকে বাতিল করুন।
যদিও আমাদের ভবিষ্যতের পর্বের জন্য এটি সংরক্ষণ করতে হবে। এটি মিস করতে চান না, এবং রুবির জাদুকরী অভ্যন্তরীণ কাজগুলিতে আরও অভিযান? রুবি ম্যাজিক ই-মেইল নিউজলেটারে সাবস্ক্রাইব করুন, নতুন নিবন্ধগুলি প্রকাশিত হওয়ার সাথে সাথে আপনার ইনবক্সে পৌঁছে দিতে।