আপনি যদি রুবি 3x3 প্রচেষ্টা ফলো করে থাকেন, তবে আপনি শুনে থাকবেন Optcarrot-এর কথা। এটি পিউর রুবিতে লিখিত একটি NES এমুলেটর।
আমি সম্প্রতি Optcarrot সোর্স কোড দেখছিলাম এবং একটি আকর্ষণীয় ডিটেইল লক্ষ্য করলাম। এটি রুবির হ্যাশের একটি ফিচারের ব্যাপক ব্যবহার করে যা প্রায়শই উপেক্ষা করা হয়, কিন্তু খুব উপকারী। সেটি হলো যেকোনো অবজেক্টকে হ্যাশ কি হিসেবে ব্যবহার করার ক্ষমতা।
প্রসঙ্গ: NES মেমরি ম্যাপিং
উচ্চ-স্তরের প্রোগ্রামার হিসেবে, আমরা মেমরিকে RAM হিসেবে চিন্তা করি। কিন্তু নিম্ন-স্তরে, "মেমরির" অনেক অন্যান্য ব্যবহার আছে।
"মেমরি" রিড এবং রাইট করা হলো NES-এর CPU GPU, কন্ট্রোল প্যাড এবং কার্ট্রিজের যেকোনো স্পেশাল ইলেকট্রনিক্সের সাথে যোগাযোগ করার পদ্ধতি। ব্যবহৃত অ্যাড্রেসের ওপর নির্ভর করে, একটি write_to_memory মেথড কল জয়স্টিক রিসেট করতে পারে, VRAM সোয়াপ করতে পারে বা সাউন্ড প্লে করতে পারে।
আপনি রুবিতে এটিকে কীভাবে ইমপ্লিমেন্ট করবেন?
Optcarrot এটি করে ৬৫৫৩৬ অ্যাড্রেসের প্রত্যেকের জন্য দুটি Method অবজেক্ট সংরক্ষণ করে। একটি গেটার এবং একটি সেটার। এটি কিছুটা এইরকম দেখায়:
@getter_methods[0x0001] = @ram.method(:[])
@setter_methods[0x0001] = @ram.method(:[]=)
সমস্যা: ডুপ্লিকেট অবজেক্ট
এইভাবে Object#method ব্যবহার করার সমস্যা হলো এটি অনেকগুলো একই Method অবজেক্ট তৈরি করে।
আমরা এটি object_id দেখে বুঝতে পারি:
> a = []
> a.method(:[]=).object_id
=> 70142391223600
> a.method(:[]=).object_id
=> 70142391912420
দুইটি Method অবজেক্টের ভিন্ন object_id মান আছে, তাই সেগুলো ভিন্ন অবজেক্ট হলেও সেগুলো একই কাজ করে।
সাধারণত, আমরা কয়েকটি অতিরিক্ত Method অবজেক্টের জন্য চিন্তা করি না, কিন্তু এই কেসে আমরা হাজার হাজার টি নিয়ে কাজ করছি।
সমাধান: হ্যাশ দ্বারা মেমোইজেশন
Optcarrot ডুপ্লিকেট Method অবজেক্ট সমস্যা একটি ট্রিক দিয়ে এড়ায় যা এতটাই সিম্পল যে সহজে উপেক্ষা করা যায়।
এটি একটি হ্যাশ ব্যবহার করে মেমোইজ এবং ডিডুপ্লিকেট করতে। নিচের সিম্পলিফাইড কোড টেকনিকটি প্রদর্শন করে:
def initialize
@setter_methods = []
@setter_cache = {}
...
end
def add_setter(address, setter)
# ডুপ্লিকেট স্টোর করে না
@setter_cache[setter] ||= setter
// ডিডুপ্লিকেটেড ভার্সন ব্যবহার করুন
@setter_methods[address] = @setter_cache[setter]
end
এটি কাজ করে কারণ Hash কেয় হিসেবে আপনি যেকোনো ধরনের অবজেক্ট দেন সেটির জন্য চিন্তা করে না।
যদি এটি বিভ্রান্তিকর লাগে, তবে IRB-এ স্ট্রিং দিয়ে ট্রাই করুন:
> cache = {}
> cache["foo"] ||= "bar"
=> "bar"
cache["foo"] ||= "baz"
=> "bar"
এখন বিবেচনা করুন যে রুবিতে, স্ট্রিংগুলো String ক্লাসের ইনস্ট্যান্স। রুবি যে মেকানিজম ব্যবহার করে স্ট্রিংকে হ্যাশ কি হিসেবে ব্যবহার করতে, এটি মূলত একই মেকানিজম যা Method অবজেক্ট স্টোর করতে ব্যবহৃত হয়।
হ্যাশ কীভাবে ইকুয়ালিটি ক্যালকুলেট করে
নন-স্ট্রিং অবজেক্ট হ্যাশ কি হিসেবে ব্যবহার করার সময় প্রশ্ন উঠে: Hash কীভাবে জানে দুটি অবজেক্ট সমান কিনা?
উত্তর হলো এটি Object#hash মেথড ব্যবহার করে। এই মেথড আপনার অবজেক্টের মধ্য দিয়ে যায় এবং রিকারসিভলি একটি হ্যাশ জেনারেট করে। এটা এইরকম দেখায়:
> a.method(:[]=).hash
=> 929915641391564853
কারণ একই অবজেক্ট একই হ্যাশ ভ্যালু তৈরি করে, তাই এটি ইকুয়ালিটির একটি টেস্ট হিসেবে ব্যবহার করা যায়।
a.hash == b.hash
আকর্ষণীয়ভাবে, এটি eql? মেথডেও একই পদ্ধতি ব্যবহৃত হয়:
a.eql?(b)
এটি আমাদের উদাহরণের Method অবজেক্টের সাথে কাজ করে:
> a.method(:[]=).hash == a.method(:[]=).hash
=> true
উপসংহার
রুবি ওয়েব ডেভেলপমেন্ট প্যাটার্নের অভ্যস্ত হয়ে যাওয়ার পর, optcarrot সোর্স দেখে কীভাবে একটি রিয়েল-টাইম নন-ওয়েব অ্যাপ আলাদা প্যাটার্ন ব্যবহার করে তা দেখা আমার জন্য খুব আকর্ষণীয় ছিল। একটি ওয়েব অ্যাপে আমি সন্দেহ করি আমি কখনো ৬৫৫৩৬ এলিমেন্টের একটি অ্যারে বানাব, কিন্তু এখানে, একটি "ডেস্কটপ" অ্যাপের সেটআপের অংশ হিসেবে, এটি অনেক সেন্স দেয়।
আপনার কোনো প্রশ্ন বা মন্তব্য থাকলে, starr@honeybadger.io বা টুইটার @StarrHorne-এ যোগাযোগ করুন।