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

เทคนิค Ruby Hash ขั้นสูง

เมื่อคุณใช้บางสิ่งมากเท่ากับนักพัฒนา Ruby ที่ใช้ Hashes คุณจะคิดว่าคุณได้เห็นมันทั้งหมดแล้ว

แต่ฉันมาที่นี่เพื่อบอกคุณว่า Hash ทับทิมผู้ต่ำต้อยมีเคล็ดลับบางอย่าง ห่างไกลจากการเป็นระบบคีย์-ค่าที่โง่เขลา ออบเจ็กต์ Hash ช่วยให้คุณทำสิ่งที่น่าสนใจและซับซ้อนได้

วัตถุใดๆ ก็สามารถเป็นคีย์แฮชได้

ก่อนที่เราจะไปไกลกว่านี้ฉันอยากจะชี้ให้เห็นสิ่งหนึ่งที่อาจไม่ชัดเจน แม้ว่าเรามักจะใช้สตริงและสัญลักษณ์เป็นแฮชคีย์ แต่ก็ไม่ได้หมายความว่าเราจะใช้อ็อบเจกต์ประเภทอื่นไม่ได้เช่นกัน อันที่จริง คุณสามารถใช้ได้เกือบทุกอย่างเป็นแฮชคีย์

# Numbers can be hash keys
{1 => "one"}[1] # "one"

# So can the Ruby kernel
{Kernel => 1}[Kernel] # 1

# You can store values for specific classes
{Kernel => 1, String => 2}["hello world".class] # 2

# You can store values for booleans
{true => "verdad"}[1==1] # "verdad"

# You can even use complex arrays and even other hashes as hash keys
{[[1,0],[0,1]] => "identity matrix"}[[[1,0], [0,1]]] # "identity matrix"

ตัวเลือกเหล่านี้บางส่วนมีประโยชน์มากกว่าตัวเลือกอื่นๆ แต่พร้อมให้คุณใช้งานแล้ว

คุณสามารถควบคุมค่าเริ่มต้นได้

สมมติว่าคุณมีแฮช h={ a: 1 } . หากคุณพยายามเข้าถึงค่าที่ไม่มีอยู่ - ตัวอย่างเช่น   h[:x] - คุณได้รับศูนย์ นั่นเป็นเพราะว่าไม่มีเป็นค่าเริ่มต้นของทุกแฮช เว้นแต่คุณจะระบุเป็นอย่างอื่น

คุณสามารถตั้งค่าเริ่มต้นสำหรับแฮชใหม่ได้โดยส่งอาร์กิวเมนต์ไปยังตัวสร้าง

h = Hash.new("This attribute intentionally left blank")
h[:a] = 1
h[:a] # 1
h[:x] # "This attribute intentionally left blank"

ค่าเริ่มต้นแบบไดนามิก

ให้ความสนใจ เพราะนี่คือเคล็ดลับที่เป็นพื้นฐานของทุกสิ่งที่ตามมา

หากคุณส่งบล็อกไปยังตัวสร้าง คุณจะสร้างค่าเริ่มต้นได้โดยทางโปรแกรม ในตัวอย่างด้านล่าง ฉันได้เพิ่มการประทับเวลาให้กับค่าเริ่มต้น ดังนั้นคุณจะเห็นได้ว่าค่านั้นถูกสร้างขึ้นแบบไดนามิก

h = Hash.new { |hash, key| "#{key}: #{ Time.now.to_i }" }
h[:a] # "a: 1435682937"
h[:a] # "a: 1435682941"
h[:b] # "b: 1435682943"

นี่เป็นสิ่งสำคัญเนื่องจากบล็อก "ค่าเริ่มต้น" สามารถทำสิ่งอื่นนอกเหนือจากการคืนค่าเริ่มต้น

การเพิ่มข้อยกเว้นหากไม่มีคีย์แฮช

ปัญหาหลักอย่างหนึ่งของแฮชคือแฮชไม่สำเร็จ คุณพิมพ์ user[:phnoe] . โดยไม่ได้ตั้งใจ แทน user[:phone] และแทนที่จะสร้างข้อยกเว้น แฮชจะคืนค่าเป็นศูนย์ แต่คุณสามารถเปลี่ยนพฤติกรรมนี้ได้

h = Hash.new { |hash, key| raise ArgumentError.new("No hash key: #{ key }") }
h[:a]=1
h[:a] # 1
h[:x] # raises ArgumentError: No hash key: x

เทคนิคนี้มีประโยชน์สำหรับการดีบักและการปรับโครงสร้างใหม่ เนื่องจากใช้กับแฮชเฉพาะ เป็นวิธีที่รบกวนน้อยกว่ามากในการเพิ่มพฤติกรรมนี้มากกว่าสิ่งที่ต้องการลิงแก้ไขคลาส Hash

หมายเหตุ:ฉันไม่ได้แนะนำให้ใครใช้สิ่งนี้แทน Hash.fetch ในโค้ดใหม่ เป็นเพียงเคล็ดลับที่น่าสนใจในการจัดเตรียมการดีบักและการจัดโครงสร้างใหม่

ตารางค้นหาที่สร้างขึ้นอย่างเกียจคร้าน

เทคนิคนี้มีประโยชน์สำหรับการแคชผลลัพธ์ของการคำนวณ ลองนึกภาพว่าคุณต้องคำนวณรากที่สองจำนวนมาก คุณสามารถสร้างตารางค้นหาแบบขี้เกียจได้ดังตัวอย่างด้านล่าง

sqrt_lookup = Hash.new { |hash, key| hash[key] = Math.sqrt(key) }
sqrt_lookup[9] # 3.0
sqrt_lookup[7] # 2.6457513110645907
sqrt_lookup    # {9=>3.0, 7=>2.6457513110645907}

ตารางค้นหาขี้เกียจแบบเรียกซ้ำ

สมมติว่าคุณมีฟังก์ชันแบบเรียกซ้ำและต้องการแคชผลลัพธ์ของการเรียกซ้ำแต่ละครั้ง ยกตัวอย่างการคำนวณแบบแฟกทอเรียล "โฟร์แฟกทอเรียล" หรือ "4!" เป็นอีกวิธีหนึ่งในการพูดว่า "4x3x2x1" คุณสามารถใช้ซ้ำได้โดยใช้แฮช ตัวอย่างด้านล่างซึ่งฉันได้นำมาจากโพสต์ในบล็อกนี้แสดงให้เห็นอย่างชัดเจน:

factorial = Hash.new do |h,k| 
  if k > 1
    h[k] = h[k-1] * k
  else
    h[k] = 1
  end
end

factorial[4] # 24
factorial    # {1=>1, 2=>2, 3=>6, 4=>24}

การแก้ไขค่าเริ่มต้นหลังจากการเริ่มต้น

คุณยังสามารถควบคุมค่าเริ่มต้นได้หลังจากสร้างแฮชแล้ว ในการดำเนินการนี้ให้ใช้ default และ default_proc เซ็ตเตอร์

h={}
h[:a] # nil
h.default = "new default"
h[:a] # "new default"

h.default_proc = Proc.new { Time.now.to_i }
h[:a] # 1435684014

Find the Ruby:เกมแฮชที่ซ้อนกันอย่างไม่สิ้นสุดอย่างเกียจคร้าน

เพื่อความสนุก เรามารวมเทคนิคที่มีประโยชน์เหล่านี้ไว้ในตัวอย่างที่ไร้ประโยชน์อย่างยิ่ง จำเกมผจญภัยแบบข้อความเก่าได้หรือไม่? มาสร้างเวอร์ชันที่โง่ที่สุดกันเถอะ

ลองนึกภาพคุณอยู่ในถ้ำ คุณสามารถไปเหนือ ใต้ ตะวันออก หรือตะวันตก สามตัวเลือกเหล่านี้จะพาคุณไปยัง "ห้อง" ใหม่ของถ้ำที่คุณสำรวจต่อไป แต่ทางเลือกหนึ่งจะนำคุณไปสู่ ​​"ทับทิม" ดังนั้นชื่อของเกม:"ค้นหาทับทิม"

แต่ละห้องในถ้ำสอดคล้องกับแฮช แฮชมีรายการเดียวเท่านั้น หนึ่งใน ["n", "s", "e", "w"] ที่ถูกสุ่มเลือก มีค่าเป็น "You found the ruby" หากคุณเลือกไม่ถูกต้อง แฮชใหม่จะถูกสร้างขึ้นและเพิ่มลงในทรี

generator = Proc.new do |hash, key| 
  hash[key] = Hash.new(&generator).merge(["n", "s", "e", "w"][rand(4)] => "You found the ruby!")
end
dungeon = Hash.new(&generator)
dungeon["n"] # <Hash ...
dungeon["n"]["s"] # <Hash ...
dungeon["n"]["s"]["w"] # "You found the ruby!"