היום למדתי: בדיקת שייכות ל Range ב Ruby

20/12/2025

תמיד השתמשתי ב include? כדי לבדוק אם מספר נמצא בטווח מסוים והקוד הזה עובד ודי יעיל:

3.3.5 :007 > (1..10).include?(5)
 => true

רובי לא מתאמץ לעבור איבר-איבר בטווח מאחר וזה טווח של מספרים שלמים ו-5 הוא מספר שלם אז מספיק לבדוק שהוא גדול מההתחלה וקטן מהסוף. אגב קל לראות את זה כשמחפשים שבר בתוך הטווח:

3.3.5 :010 > (1..10).include?(4.5)
 => true

כשברור שאם ננסה לעבור איבר-איבר לא נמצא את ארבע וחצי:

3.3.5 :011 > (1..10).to_a.include?(4.5)
 => false

השבוע נתקלתי במקרה בפונקציה conver? שנראתה כאילו עושה אותו דבר:

3.3.5 :008 > (1..10).cover?(5)
 => true
3.3.5 :009 > (1..10).cover?(4.5)
=> true

גם היא לא צריכה לרוץ על כל הטווח ומסתפקת בבדיקת קצוות. אז איפה זה מתחיל להיות מעניין? מסתבר שאנחנו צריכים 2 פונקציות כדי לטפל אחרת בטווחים של דברים יותר מסובכים ממספרים, למשל מחרוזות. ברובי גם זה טווח:

3.3.5 :013 > ('a'..'z').class
 => Range

וכמו עם שברים גם בטווח של מחרוזות אנחנו יכולים לבדוק שמחרוזת מסוימת נמצאת בין ההתחלה לסוף:

3.3.5 :014 > ('a'..'z').include?('t')
 => true
3.3.5 :015 > ('a'..'z').cover?('t')
 => true

את ההבדל בין cover ל include אנחנו מגלים כשמנסים לחפש מחרוזת שנמצאת בטווח אבל לא תופיע ברשימה אם נהפוך את הטווח למערך:

3.3.5 :018 > ('a'..'d').to_a
 => ["a", "b", "c", "d"]
3.3.5 :019 > ('a'..'d').include?('bob')
 => false
3.3.5 :020 > ('a'..'d').cover?('bob')
 => true

עכשיו צודקים מי שיגידו שזה לא הוגן כלפי השברים עם הנקודה העשרונית, הרי הם היו צריכים לקבל את אותו יחס של מחרוזות. צודקים גם אם תגידו שזה מבלבל שאותה פונקציה include? לפעמים מחזירה תוצאה ב O(1) ופעמים אחרות מחזירה את התוצאה ב O(n). אני יכול רק לענות שהחיים לא הוגנים ורובי באמת יכולה להיות לא עקבית אבל טוב שגיליתי את cover? שייחודית לטווחים ותמיד מחזירה תשובה ב O(1).