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

เชี่ยวชาญการจัดเรียง Ruby: เทคนิค Array และ Hash พร้อมตัวอย่างปฏิบัติ

การจัดเรียงอาร์เรย์ใน Ruby ทำได้ง่ายมาก!

คุณ ไม่ต้องเขียนอัลกอริทึมซับซ้อน เพื่อให้ได้ผลลัพธ์ที่ต้องการ

ต้องการอะไรบ้าง?

เข้าใจเมธอดจัดเรียง built-in ของ Ruby

คือเมธอดเหล่านี้:

  • sort
  • sort_by
  • sort!

เมธอดเหล่านี้ทำงานอย่างไร และต่างกันยังไง?

นั่นคือสิ่งที่คุณจะค้นพบในบทความนี้

คุณจะเรียนรู้วิธีจัดเรียงอาร์เรย์ต่างๆ เริ่มจาก sort แล้วค่อยไปดู sort_by สำหรับการจัดเรียงขั้นสูง (หลายค่า) และอื่นๆ

มาเริ่มกัน!

เรียนรู้การใช้เมธอด Sort และ Sort! ใน Ruby

รูปแบบพื้นฐานสุดของการจัดเรียงมาจากเมธอด sort ของ Ruby ซึ่งถูกกำหนดโดยโมดูล Enumerable

มาดูตัวอย่าง:

 numbers = [5,3,2,1] numbers.sort # [1,2,3,5] 

สังเกตว่า sort จะคืนอาร์เรย์ใหม่พร้อมผลลัพธ์

อาร์เรย์ของอิลิเมนต์ที่จัดเรียงแล้ว!

ยังสามารถจัดเรียง "in-place" ได้ด้วยเมธอด sort!

หมายความว่า อาร์เรย์เดิมจะ เปลี่ยนแปลง แทนที่จะสร้างใหม่ ซึ่งอาจดีต่อประสิทธิภาพ

การจัดเรียงแบบกำหนดเองด้วย sort_by

ด้วยเมธอด sort_by คุณทำการจัดเรียงขั้นสูงและน่าสนใจได้

คุณจะสามารถ:

  • จัดเรียงตามความยาวสตริง
  • จัดเรียงตามเนื้อหาสตริง
  • จัดเรียงตามว่าตัวเลขเป็นคู่หรือคี่

ทำได้ด้วย sort_by และบล็อก Ruby

เช่น:

 strings = %w(foo test blog a) strings.sort_by(&:length) # ["a", "foo", "test", "blog"] 

มันทำงานยังไง?

เมธอด sort_by คาดหวังค่าตัวเลข นั่นคือเหตุผลที่ length ใช้ได้

ถ้าคุณเข้าใจนี้ คุณสามารถใช้เมธอดนี้ทำอย่างน่าสนใจได้ เช่น จัดเรียงคำที่ขึ้นต้นด้วยตัวใหญ่ และทิ้งส่วนที่เหลือไว้ที่เดิม

แบบนี้:

 def sort_by_capital_word(text) text .split .sort_by { |w| w[0].match?(/[A-Z]/) ? 0 : 1 } .join(" ") end sort_by_capital_word("calendar Cat tap Lamp") # "Cat Lamp calendar tap" 

ยังสามารถจัดเรียงแบบกำหนดเองด้วย sort ธรรมดาและบล็อกได้

ตัวอย่าง:

 strings = %w(foo test blog a) strings.sort { |a,b| a.length <=> b.length } # ["a", "foo", "test", "blog"] 

หมายเหตุ: สัญลักษณ์ <=> เรียกว่า "spaceship operator" เป็นเมธอดที่คุณสามารถนำไปใช้ในคลาสของตัวเองได้ ควรคืนค่า 1 (มากกว่า), 0 (เท่ากัน) หรือ -1 (น้อยกว่า)

โดยทั่วไป ผมชอบ sort_by มากกว่า เพราะเจตนาชัดเจนกว่า อ่านง่ายกว่า และเร็วกว่าเล็กน้อย

จัดเรียงย้อนกลับ (Reverse Order)

การจัดเรียงย้อนกลับทำยังไง?

คุณอาจใช้ reverse หลังจัดเรียง หรือใช้บล็อกและวางเครื่องหมายลบหน้าสิ่งที่จัดเรียง

ตัวอย่าง:

 strings = %w(foo test blog a) strings.sort_by { |str| -str.length } # ["blog", "test", "foo", "a"] 

ไม่หรูหรา แต่ใช้ได้! 🙂

การจัดเรียงอัลฟา न्यूमอริก (Alphanumeric Sorting)

สมมติอยากจัดเรียงตัวเลขในลิสต์สตริงที่มีตัวเลข

แบบนี้:

 music = %w(21.mp3 10.mp3 5.mp3 40.mp3) 

ค่าเริ่มต้นจะไม่ได้ลิสต์ที่จัดเรียงเหมือนที่อยากได้

ตัวอย่าง:

 music.sort # ["10.mp3", "21.mp3", "40.mp3", "5.mp3"] 

แต่แก้ได้ด้วย sort_by:

 music.sort_by { |s| s.scan(/\d+/).first.to_i } # ["5.mp3", "10.mp3", "21.mp3", "40.mp3"] 

ผมใช้ regular expression (\d+) จับตัวเลข แล้วเอาตัวแรก (first) และแปลงเป็น integer (to_i)

วิธีจัดเรียง Hash ใน Ruby

คุณไม่จำกัดแค่อาร์เรย์ สามารถจัดเรียง hash ได้

ตัวอย่าง:

 hash = {coconut: 200, orange: 50, bacon: 100} hash.sort_by(&:last) # [[:orange, 50], [:bacon, 100], [:coconut, 200]] 

นี่จัดเรียงตามค่า แต่สังเกตสิ่งน่าสนใจ: สิ่งที่ได้กลับมา ไม่ใช่ hash

คุณได้อาร์เรย์หลายมิติ (multi-dimensional array) ตอนจัดเรียง hash

เพื่อแปลงกลับเป็น hash ใช้เมธอด Array#to_h

การจัดเรียงหลายค่า (Sorting By Multiple Values)

คุณอาจอยากจัดเรียงหลายแอตทริบิวต์ คือจัดเรียงตามวันที่ก่อน (เช่น) แต่มีหลายอย่างที่วันที่เดียวกันจึงมีการเสมอ (tie)

เพื่อแยก tie ใช้แอตทริบิวต์รอง

ตัวอย่าง:

 Event = Struct.new(:name, :date) events = [] events << Event.new("book sale", Time.now) events << Event.new("course sale", Time.now) events << Event.new("new subscriber", Time.now) events << Event.new("course sale", Time.now + 1.day) events.sort_by { |event| [event.date, event.name] } 

กุญแจสำคัญคืออาร์เรย์ภายในบล็อก sort_by

คุณตั้งแอตทริบิวต์หลักเป็นอิลิเมนต์แรก (event.date) และแอตทริบิวต์รองสำหรับแยก tie (event.name)

การนำ QuickSort ไปใช้งาน

เพื่อความสนุก ลองนำ sorting method ของตัวเองมาใช้งาน นี่จะช้ากว่า built-in methods แต่เป็นแบบฝึกหัดน่าสนใจถ้าชอบ computer science

 def quick_sort(list) return [] if list.empty? groups = list.group_by { |n| n <=> list.first } less_than = groups[-1] || [] first = groups[0] || [] greater_than = groups[1] || [] quick_sort(less_than) + first + quick_sort(greater_than) end p quick_sort [3, 7, 2, 1, 8, 12] # [1, 2, 3, 7, 8, 12] 

แนวคิด quick sort คือ เลือกตัวเลขสุ่ม แล้วแบ่งลิสต์ที่จัดเรียงเป็น 2 กลุ่ม

กลุ่มหนึ่งคือตัวเลขน้อยกว่าตัวที่เลือก อีกกลุ่มคือตัวเลขมากกว่า

แล้วทำซ้ำจนกว่าลิสต์จะเรียงแล้ว

Benchmarks

มาเปรียบเทียบเมธอดจัดเรียงทั้งหมดด้านประสิทธิภาพ

Ruby 2.4.0:

 sort!: 1405.8 i/s sort: 1377.6 i/s - same-ish: difference falls within error sort_by reverse: 196.6 i/s - 7.15x slower sort_by: 183.7 i/s - 7.65x slower sort_by minus: 172.3 i/s - 8.16x slower sort with block: 164.1 i/s - 8.57x slower 

เห็นได้ว่า sort ธรรมดาเร็วกว่า sort_by มาก แต่ไม่ยืดหยุ่นเท่าไรนัก เว้นแต่ใช้บล็อก

วิดีโอ

สรุป

คุณเรียนรู้การใช้ sort และ sort_by จัดเรียงอาร์เรย์และแฮชแบบต่างๆ รู้ความต่างด้านประสิทธิภาพ และนำ quicksort ไปใช้งาน

สิ่งที่ควรจำ:

  • ใช้ sort บนอาร์เรย์ แฮช หรือ Enumerable อื่น ได้พฤติกรรมเริ่มต้น (จัดเรียงตามออปเปอเรเตอร์ <=>)
  • ใช้ sort กับบล็อก และอาร์กิวเมนต์ 2 ตัว กำหนดว่าออบเจกต์หนึ่งต่างจากอีกอันอย่างไร (บล็อกควรคืน 1, 0, หรือ -1)
  • ใช้ sort_by กับบล็อก และอาร์กิวเมนต์ 1 ตัว กำหนดแอตทริบิวต์หนึ่งต่อออบเจกต์หนึ่ง เป็นพื้นฐานการจัดเรียง (ความยาวอาร์เรย์, แอตทริบิวต์ออบเจกต์, index, ฯลฯ) บล็อกควรคืนค่า integer กำหนดตำแหน่งในอาร์เรย์ที่จัดเรียงแล้ว

อย่าลืม แชร์โพสต์นี้ ให้คนอื่นเรียนรู้ได้ 🙂