היום למדתי: בדיקת שייכות ל Range ב Ruby
תמיד השתמשתי ב 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).