Memoization হল একটি কৌশল যা আপনি আপনার অ্যাক্সেসর পদ্ধতির গতি বাড়ানোর জন্য ব্যবহার করতে পারেন। এটি এমন পদ্ধতির ফলাফল ক্যাশ করে যা সময়সাপেক্ষ কাজ করে, কাজ যা শুধুমাত্র একবার করা দরকার। রেলে, আপনি দেখতে পাচ্ছেন যে মেমোাইজেশন এত ঘন ঘন ব্যবহার করা হয়েছে যে এটিতে এমন একটি মডিউলও অন্তর্ভুক্ত রয়েছে যা আপনার জন্য মেমোাইজ করার পদ্ধতিগুলিকে অন্তর্ভুক্ত করবে৷
পরে, এটিকে বিতর্কিতভাবে সরানো হয়েছিল শুধুমাত্র সত্যিকারের সাধারণ মেমোাইজেশন প্যাটার্ন ব্যবহার করার পক্ষে যা আমি প্রথমে বলব। কিন্তু আপনি যেমন দেখতে পাবেন, এমন কিছু জায়গা আছে যেখানে এই মৌলিক প্যাটার্নটি ঠিক কাজ করে না৷ তাই আমরা আরও উন্নত মেমোাইজেশন প্যাটার্নগুলিও দেখব এবং প্রক্রিয়ায় রুবি সম্পর্কে কিছু পরিষ্কার জিনিস শিখব!
সুপার বেসিক মেমোাইজেশন
আপনি রুবিতে এই মেমোাইজেশন প্যাটার্নটি সব সময় দেখতে পাবেন:
class User < ActiveRecord::Base
def twitter_followers
# assuming twitter_user.followers makes a network call
@twitter_followers ||= twitter_user.followers
end
end
||=
কমবেশি অনুবাদ করে @twitter_followers = @twitter_followers || twitter_user.followers
. এর মানে হল যে আপনি প্রথমবার twitter_followers
কল করলেই আপনি নেটওয়ার্ক কল করবেন , এবং ভবিষ্যতের কলগুলি শুধুমাত্র ইনস্ট্যান্স ভেরিয়েবলের মান ফেরত দেবে @twitter_followers
.
মাল্টি-লাইন মেমোাইজেশন
কখনও কখনও, ধীর কোড ভয়ানক কিছু না করে এক লাইনে ফিট হবে না৷ কোডের একাধিক লাইনের সাথে কাজ করার জন্য মৌলিক প্যাটার্ন প্রসারিত করার কয়েকটি উপায় আছে, কিন্তু এটি আমার প্রিয়:
class User < ActiveRecord::Base
def main_address
@main_address ||= begin
maybe_main_address = home_address if prefers_home_address?
maybe_main_address = work_address unless maybe_main_address
maybe_main_address = addresses.first unless maybe_main_address
end
end
end
begin...end
রুবিতে কোডের একটি ব্লক তৈরি করে যেটিকে একক জিনিস হিসাবে বিবেচনা করা যেতে পারে, যেমন {...}
সি-স্টাইলের ভাষায়। এজন্যই ||=
এখানেও ঠিক তেমনই কাজ করে যেমন এটি আগে করেছিল৷
শূন্য সম্পর্কে কি?
কিন্তু এই মেমোাইজেশন প্যাটার্নগুলির একটি বাজে, লুকানো সমস্যা আছে। প্রথম উদাহরণে, ব্যবহারকারীর টুইটার অ্যাকাউন্ট না থাকলে এবং টুইটার ফলোয়ার API nil
ফেরত দিলে কী হবে ? দ্বিতীয়টিতে, ব্যবহারকারীর কোনো ঠিকানা না থাকলে এবং ব্লকটি nil
ফেরত দিলে কী হবে ?
যতবার আমরা পদ্ধতিটি কল করব, ইনস্ট্যান্স ভেরিয়েবলটি হবে nil
, এবং আমরা আবার ব্যয়বহুল ফেচগুলি সম্পাদন করব৷৷
তাই, ||=
সম্ভবত যেতে সঠিক উপায় নয়. পরিবর্তে, আমাদের nil
এর মধ্যে পার্থক্য করতে হবে এবং undefined
:
class User < ActiveRecord::Base
def twitter_followers
return @twitter_followers if defined? @twitter_followers
@twitter_followers = twitter_user.followers
end
end
class User < ActiveRecord::Base
def main_address
return @main_address if defined? @main_address
@main_address = begin
main_address = home_address if prefers_home_address?
main_address ||= work_address
main_address ||= addresses.first # some semi-sensible default
end
end
end
দুর্ভাগ্যবশত, এটি একটু কুৎসিত, কিন্তু এটি nil
এর সাথে কাজ করে , false
, এবং অন্য সবকিছু। (nil
পরিচালনা করতে ক্ষেত্রে, আপনি এই সমস্যা এড়াতে Null অবজেক্ট এবং খালি অ্যারে ব্যবহার করতে পারেন। nil
এড়ানোর আরও একটি কারণ !)
এবং প্যারামিটার সম্পর্কে কি?
আমাদের কিছু মেমোাইজেশন প্যাটার্ন রয়েছে যা সাধারণ অ্যাক্সেসরদের জন্য ভাল কাজ করে। কিন্তু আপনি যদি এমন একটি পদ্ধতি মেমোাইজ করতে চান যাতে প্যারামিটার লাগে, যেমন এই পদ্ধতি?
class City < ActiveRecord::Base
def self.top_cities(order_by)
where(top_city: true).order(order_by).to_a
end
end
দেখা যাচ্ছে যে রুবির Hash
একটি ইনিটালাইজার আছে যা পুরোপুরি কাজ করে এই অবস্থার জন্য। আপনি Hash.new
কল করতে পারেন একটি ব্লক সহ:
Hash.new {|h, key| h[key] = some_calculated_value }
তারপরে, আপনি যখনই হ্যাশের একটি কী অ্যাক্সেস করার চেষ্টা করবেন যা সেট করা হয়নি, এটি ব্লকটি কার্যকর করবে। এবং আপনি ব্লকে অ্যাক্সেস করার চেষ্টা করেছেন এমন কী সহ এটি হ্যাশ নিজেই পাস করবে।
সুতরাং, আপনি যদি এই পদ্ধতিটি মুখস্থ করতে চান, আপনি এরকম কিছু করতে পারেন:
class City < ActiveRecord::Base
def self.top_cities(order_by)
@top_cities ||= Hash.new do |h, key|
h[key] = where(top_city: true).order(key).to_a
end
@top_cities[order_by]
end
end
এবং আপনি order_by
এ যা পাস করুন না কেন , সঠিক ফলাফল মুখস্থ করা হবে। যেহেতু ব্লকটি কেবল তখনই বলা হয় যখন কীটি বিদ্যমান থাকে না, তাই ব্লকটি শূন্য বা মিথ্যা হওয়ার ফলাফল নিয়ে আপনাকে চিন্তা করতে হবে না।
আশ্চর্যজনকভাবে, Hash
আসলে অ্যারে যে কীগুলির সাথে ঠিক কাজ করে:
h = {}
h[["a", "b"]] = "c"
h[["a", "b"]] # => "c"
সুতরাং আপনি এই প্যাটার্নটি যেকোন সংখ্যক প্যারামিটার সহ পদ্ধতিতে ব্যবহার করতে পারেন!
কেন এত কষ্টের মধ্য দিয়ে যাচ্ছি?
অবশ্যই, আপনি যদি অনেকগুলি পদ্ধতিতে এই মেমোাইজেশন প্যাটার্নগুলি যোগ করা শুরু করেন, আপনার কোডটি খুব দ্রুত অপাঠ্য হয়ে উঠবে। আপনার পদ্ধতি সমস্ত অনুষ্ঠান এবং কোন পদার্থ হবে না.
সুতরাং আপনি যদি এমন একটি অ্যাপে কাজ করেন যার প্রচুর মেমোাইজেশনের প্রয়োজন হয়, তাহলে আপনি এমন একটি রত্ন ব্যবহার করতে চাইতে পারেন যা আপনার জন্য একটি সুন্দর, বন্ধুত্বপূর্ণ API সহ মেমোাইজেশন পরিচালনা করে৷ Memoist একটি ভাল এক বলে মনে হচ্ছে, এবং চমত্কার অনুরূপ Rails কি ছিল. (অথবা, আপনার নতুন পাওয়া মেমোাইজেশন জ্ঞানের সাথে, আপনি নিজেও একটি তৈরি করার চেষ্টা করতে পারেন)।
কিন্তু এই ধরনের নিদর্শনগুলি অনুসন্ধান করা সর্বদা আকর্ষণীয়, দেখুন কিভাবে তারা একত্রিত হয়, তারা কোথায় কাজ করে এবং তীক্ষ্ণ প্রান্তগুলি কোথায়। এবং আপনি অন্বেষণ করার সময় কিছু কম পরিচিত রুবি বৈশিষ্ট্য সম্পর্কে কিছু ঝরঝরে জিনিস শিখতে পারেন৷