Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ออบเจ็กต์เป็น Ruby Hash Keys

หากคุณได้ติดตามความพยายามของ 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 มาก่อน แต่ที่นี่เป็นส่วนหนึ่งของการตั้งค่าแอป "เดสก์ท็อป" มันสมเหตุสมผลมาก

หากคุณมีคำถามหรือความคิดเห็น โปรดติดต่อที่ starr@honeybadger.io หรือ @StarrHorne บน Twitter