কম্পিউটার

রুবি মধ্যে গণনা ভিতরে

রুবি ম্যাজিকের আরেকটি সংস্করণে আবার স্বাগতম! এক বছর আগে, আমরা রুবির 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 নিয়োগ করতে পারি শেষ মুহূর্ত পর্যন্ত লুপ করতে দেরি করতে, এবং ডুপ্লিকেট তালিকার বিপরীতে নিজেকে বাতিল করুন।

যদিও আমাদের ভবিষ্যতের পর্বের জন্য এটি সংরক্ষণ করতে হবে। এটি মিস করতে চান না, এবং রুবির জাদুকরী অভ্যন্তরীণ কাজগুলিতে আরও অভিযান? রুবি ম্যাজিক ই-মেইল নিউজলেটারে সাবস্ক্রাইব করুন, নতুন নিবন্ধগুলি প্রকাশিত হওয়ার সাথে সাথে আপনার ইনবক্সে পৌঁছে দিতে।


  1. রুবিতে বিটওয়াইজ হ্যাক

  2. রুবিতে ল্যাম্বডাস ব্যবহার করা

  3. রুবিতে সন্নিবেশ বাছাই বোঝা

  4. রুবি 2.6-এ 9টি নতুন বৈশিষ্ট্য