หากคุณได้ติดตามความพยายามของ Ruby 3x3 คุณอาจเคยได้ยินเกี่ยวกับ Optcarrot มันเป็นโปรแกรมจำลอง NES ที่เขียนด้วย Ruby บริสุทธิ์
เมื่อเร็ว ๆ นี้ฉันได้ดูแหล่งที่มาของ Optcarrot และมีรายละเอียดที่น่าสนใจอย่างหนึ่งที่ติดอยู่กับฉัน ใช้ประโยชน์จากคุณลักษณะของแฮชของ Ruby อย่างกว้างขวางซึ่งมักถูกมองข้าม แต่มีประโยชน์มาก นั่นคือความสามารถในการใช้วัตถุใด ๆ เป็นคีย์แฮช
บริบท:การแมปหน่วยความจำ NES
ในฐานะโปรแกรมเมอร์ระดับสูง เรามักจะคิดว่าหน่วยความจำเป็นแรม แต่ในระดับที่ต่ำกว่า "หน่วยความจำ" มีประโยชน์อื่นๆ มากมาย
การอ่านและเขียน "หน่วยความจำ" เป็นวิธีที่ CPU ของ NES สื่อสารกับ GPU, แผ่นควบคุม และอุปกรณ์อิเล็กทรอนิกส์พิเศษใดๆ บนคาร์ทริดจ์ ขึ้นอยู่กับที่อยู่ที่ใช้ write_to_memory
วิธีการโทรสามารถรีเซ็ตจอยสติ๊ก, สลับ VRAM หรือเล่นเสียง
คุณจะนำสิ่งนี้ไปใช้กับ Ruby อย่างไร
Optcarrot ทำได้โดยเก็บ Method
. สองอัน ออบเจ็กต์สำหรับที่อยู่ 65536 แต่ละรายการ คนหนึ่งเป็นคนรับ อีกคนเป็นคนรับ หน้าตาประมาณนี้:
@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)
# Doesn't store duplicates
@setter_cache[setter] ||= setter
# Use the deduped version
@setter_methods[address] = @setter_cache[setter]
end
ใช้งานได้เพราะ Hash
ไม่สนใจว่าคุณจะมอบสิ่งของประเภทใดให้เป็นกุญแจ
หากทำให้เกิดความสับสน ลองใช้ IRB ด้วยสตริง:
> cache = {}
> cache["foo"] ||= "bar"
=> "bar"
cache["foo"] ||= "baz"
=> "bar"
ตอนนี้ ให้พิจารณาว่าใน Ruby สตริงคืออินสแตนซ์ของคลาส String
. กลไกที่ Ruby เคยใช้สตริงเป็น hash key นั้นโดยพื้นฐานแล้วเหมือนกับกลไกที่ใช้เก็บ Method
วัตถุ.
วิธีที่แฮชคำนวณความเท่าเทียมกัน
เมื่อใช้ออบเจ็กต์ที่ไม่ใช่สตริงเป็นแฮชคีย์ คำถามจะเกิดขึ้น:Hash
. เป็นอย่างไร รู้ว่าวัตถุสองชิ้นเท่ากันหรือไม่
คำตอบคือมันใช้ Object#hash
กระบวนการ. วิธีนี้จะผ่านวัตถุของคุณและสร้างแฮชแบบเรียกซ้ำ ดูเหมือนว่านี้:
> a.method(:[]=).hash
=> 929915641391564853
เนื่องจากวัตถุที่เหมือนกันจะสร้างค่าแฮชที่เหมือนกัน จึงสามารถใช้เป็นการทดสอบความเท่าเทียมกันได้
a.hash == b.hash
น่าสนใจ นี่เป็นแนวทางเดียวกับที่ eql?
. ใช้ วิธีการ:
a.eql?(b)
ใช้ได้กับ Method
วัตถุในตัวอย่างของเรา:
> a.method(:[]=).hash == a.method(:[]=).hash
=> true
บทสรุป
เมื่อคุ้นเคยกับรูปแบบการพัฒนาเว็บของ Ruby แล้ว เป็นเรื่องที่น่าสนใจมากสำหรับฉันที่จะดูแหล่งที่มาของ optcarrot และดูว่าแอปที่ไม่ใช่เว็บแบบเรียลไทม์ใช้รูปแบบต่างๆ กันอย่างไร ในเว็บแอป ฉันสงสัยว่าฉันจะเคยสร้างอาร์เรย์ที่มีองค์ประกอบ 65536 มาก่อน แต่ที่นี่เป็นส่วนหนึ่งของการตั้งค่าแอป "เดสก์ท็อป" มันสมเหตุสมผลมาก
หากคุณมีคำถามหรือความคิดเห็น โปรดติดต่อที่ [email protected] หรือ @StarrHorne บน Twitter