Range ใน Ruby คืออะไร และทำงานอย่างไร?
Range คือ object ที่มีค่าเริ่มต้นและค่าสิ้นสุด ช่วยสร้าง sequence ที่ครอบคลุมช่วงระหว่างสองค่านี้ คุณสามารถสร้างได้จาก 1 ถึง 20 หรือจาก "a" ถึง "z"
ในบทความนี้คุณจะค้นพบ:
- วิธีสร้าง Ruby range
- วิธีใช้เมธอด
stepและinclude?ใน Ruby - Range ทำงานภายในอย่างไร
มาเริ่มกัน!
ทำความเข้าใจ Ranges
เพื่อเตือนใจ: syntax ของ Ruby range 長得像這樣:
(1..20)
วงเล็บไม่จำเป็นต้องมีในการกำหนด Range แต่ถ้าคุณต้องการเรียกเมธอดบน range จะต้องมีวงเล็บ มิฉะนั้นคุณจะเรียกเมธอดบน element ตัวที่ 2 ของ range แทนที่จะเป็น range เอง
Class Range include Enumerable ดังนั้นคุณได้เมธอด iteration อันทรงพลังทั้งหมดโดยไม่ต้องแปลง range เป็น array
เมธอด Step ของ Ruby
Range มีเมธอดที่มีประโยชน์ เช่น step:
ตัวอย่าง:
(10..20).step(2).to_a # [10, 12, 14, 16, 18, 20]
ใช้เมธอดนี้เพื่อ iterate ผ่าน range ด้วยการเพิ่มทีละ n โดย n คือ argument ที่ส่งให้ step
วิธีตรวจสอบว่าเลขอยู่ใน Range หรือไม่
เมธอด Range อื่นๆ ที่ควรทราบ: cover? และ include?
จะผิดใหญ่ถ้าคิดว่าทำงานเหมือนกัน เพราะมันไม่เหมือนกันเลย
เมธอด include? ทำตามที่คาดไว้: ตรวจสอบการรวมอยู่ใน range เทียบเท่ากับการขยาย Range เป็น Array แล้วเช็กว่ามีอยู่ไหม
แต่ cover? แตกต่างออกไป มันเพียง ตรวจสอบกับค่าเริ่มต้นและค่าสิ้นสุดของ range (begin <= obj <= end) ซึ่งอาจให้ผลลัพธ์ที่ไมคาดคิด
ตัวอย่าง:
('a'..'z').include? "cc" # false
('a'..'z').cover? "cc" # true
cover? example นี้เทียบเท่ากับ:
"a" <= "cc" && "cc" <= "z"
เหตุผลที่คืนค่า true คือ string ถูกเปรียบเทียบตัวอักษรทีละตัว เนื่องจาก "a" อยู่ก่อน "c" ตัวอักษรหลัง "c" ตัวแรกจึงไม่มีผล
Ranges ถูก implement ใน Ruby อย่างไร
Range ไม่จำกัดแค่ตัวเลขและตัวอักษร คุณสามารถใช้ object ใดๆ ได้ ตราบใดที่มัน implement เมธอดต่อไปนี้: <=> และ succ
เช่น DateTime range:
require 'time' t1 = DateTime.new t2 = DateTime.new + 30 next_30_days = t1..t2 # ตัวอย่างการใช้ next_30_days.select(&:friday?).map(&:day)
แล้วมันทำงานอย่างไร? มาดู implementation นี้:
def range(a, b)
# ถ้า element แรกใหญ่กว่าตัวที่สอง ไม่ใช่ sequential range
return [] if a > b
out = []
# advance จนกว่า element ตัวที่สองจะเท่ากับตัวแรก
while a != b
out << a
a = a.next
end
# เพิ่ม element สุดท้าย (inclusive range)
# นี่ก็คือการ return ผลลัพธ์ผ่าน implicit return
out << a
end
p range 1, 10
p range 'a', 'z'
ผมเพิ่มคอมเมนต์ช่วยให้เข้าใจง่ายขึ้น แนวคิดคือเราเรียกเมธอด next บน object ตัวแรกซ้ำๆ จนกว่าจะเท่ากับตัวที่สอง โดยสมมติว่ามันจะเจอกันที่สุด
Custom Class Ranges
ส่วนใหญ่คุณจะใช้ number และ character ranges แต่รู้จักวิธีใช้ range ใน custom class ก็ดี
ตัวอย่าง:
class LetterMultiplier
attr_reader :count
include Comparable
def initialize(letter, count)
@letter = letter
@count = count
end
def succ
self.class.new(@letter, @count + 1)
end
def <=>(other)
count <=> other.count
end
end
a = LetterMultiplier.new('w', 2)
b = LetterMultiplier.new('w', 8)
# พิมพ์ array ที่มีทุก item ใน range
p Array(a..b)
หัวใจสำคัญคือ implement <=> และ succ ให้ถูกต้อง ถ้าต้องการใช้ include? ต้อง include module Comparable ซึ่งเพิ่มเมธอดอย่าง ==, <, > (ทั้งหมด based on ผลลัพธ์ของ <=>)
สรุป
ในบทความนี้คุณเรียนรู้ว่า range ทำงานใน Ruby อย่างไร เพื่อให้เข้าใจลึกขึ้นและ implement object ของตัวเองที่รองรับ range operations ได้
อย่าลืมสมัครรับ newsletter ด้านล่างเพื่อไม่พลาดบทความถัดไป 🙂