כלים שכל מפתח חייב להכיר

"חגורת הכלים" שכל מפתח צריך – חלק א'

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

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

Base 64

מה זה? אלגוריתם המרת מידע בינארי לייצוג טקטואלי, ולהפך.

מתי משתמשים? כאשר רוצים להעביר מידע בינארי בתווך התומך במידע טקסטואלי בלבד.

הסבר מפורט

נניח שהתוכנית שלנו מחזיקה מידע בינארי (לא טקסטואלי) כלשהו, למשל קובץ תמונה או מפתח הצפנה, ויש צורך לשמור או לשלוח אותו אותו למקום התומך במידע טקסטואלי (printable ASCII) בלבד, כגון דואר אלקטרוני, שדה טקסטואלי ב-DB או קובץ HTML.

במקרה זה עלינו להמיר את המידע הבינארי לייצוג טקסטואלי כלשהו טרם השליחה. מכיוון שמתוך 128 הבתים בקידוד ASCII, רק 95 הם תווים טקסטואליים, נצטרך להשתמש ביותר תווים כדי לקודד את אותו המידע (למשל, כל תו בינארי יהפוך לשני תווים טקסטואלים). אך באיזה אלגוריתם נשתמש בכדי לבצע את הקידוד? ולמה שבכלל נצטרך להתעסק עם זה?

בסיס 64 (או בעגת המתכנתים – "בייס שישים וארבע"), הוא האלגוריתם הנפוץ ביותר לביצוע המרה של מידע בינארי לטקסטואלי ולהיפך. הוא עושה זאת ע"י המרה של כל 3 בתים בינאריים (224 אפשרויות) לארבעה תווים טקסטואלים מהטווח a-z, A-Z, 0-9 ועוד 2 תווים נוספים, לרוב פלוס (+) וסלאש (/) – שהם סה"כ 64 תווים.

הנה דוגמה לשימוש בספריית base 64 בשפת python, בה היא חלק מהספרייה הסטנדרטית:

import base64
print base64.b64encode('some data') # prints c29tZSBkYXRh

ביטויים רגולריים – Regular Expressions

מה זה? תשתית חיפוש בטקסט ע"י הגדרת תבניות בצורה פשוטה (יחסית)

מתי משתמשים? כאשר רוצים לבצע חיפוש או החלפה של ביטוי טקטואלי שחוקיותו ידועה מראש, למשל "מצא את כל המילים בנות 4-5 אותיות המתחילות באות c".

הסבר מפורט

נסו לחשוב בראש על אלגוריתם המקבל מחרוזת ומוצא את כל כתובות האימייל בו. נשמע מסובך? גם לי… עכשיו במקום לנסות ולכתוב אלגוריתם כזה, תארו לעצמכם שהייתם יכולים להגדיר איך נראית כתובת אימייל ("מילה ולאחריה התו @, ולאחר מכן שתי מילים או יותר וביניהן התו נקודה"), ומנוע כלשהו היה מבצע את החיפוש בטקסט.

מנגנון ביטויים רגולריים (או regex – קיצור של regular expressions) מבצע בדיוק את זה – הוא מאפשר לנו להגדיר תבניות בשפה סטנדרטית ולחפש אותן בטקסט. לדוגמה, כך נראה חיפוש של כל המספרים בני 3 ספרות:

import re
print re.findall(r"\b\d{3}\b", the_text)

לא אכנס לאיך מגדירים ביטויים רגולריים, אבל אני ממליץ בחום על המדריך הזה.

סריאליזציה -Serialization

מה זה? המרת אובייקטים בתוכנית לצורה אותה ניתן לאחסן (לרוב טקסט), ולהפך

מתי משתמשים? כאשר רוצים לשמור את המצב הנוכחי של חלקים בתוכנית שלנו ולשלוח אותם / לטעון אותם מאוחר יותר

הסבר מפורט

נניח שיש לנו מחלקה בשם Settings המכילה (באופן בלתי צפוי…) הגדרות, אותן אנו רוצים לשמור לקובץ ולטעון אותו בעלייה מחדש של התוכנית. ההמרה של האובייקט מאוסף של תאים בזיכרון – לטקסט אותו ניתן לשמור לקובץ נקראת סריאליזציה.

אז למה זה מעניין? בעיקר כי מדובר בתהליך נפוץ עד כדי כך שבכל שפת תכנות קיימות מספר ספריות המבצעות סריאליזציה של אובייקטים (וגם את התהליך ההפוך – דה-סריאליזציה, deserialization). בנוסף, קיימים כמה פורמטים נפוצים וסטנדרטיים, אליהם נהוג לסרלז אוביקטיים, ביניהם: JSON, XML, YAMLProtobuf. לכל אחד מהפורמטים הללו יתרונות וחסרונות משלו, ומומלץ להכיר אותם כדי לבחור את המתאים ביותר לכל מטרה.

לדוגמה, נניח שנרצה לסרלז את המחלקה הבאה (C#):

public class Settings {
    public int ScreenWidth { get; set; }
}

ניתן להשתמש בספרייה Json.NET כדי להמיר מופע של המחלקה למחרוזת בפורמט JSON:

Settings settings = new Settings {ScreenWidth = 1024};
String settings_str = JsonConvert.SerializeObject(settings);

המחרוזת שתתקבל היא:

{"ScreenWidth":1024}

ובצורה דומה ניתן להמיר את המחרוזת חזרה לאובייקט:

Settings settings = JsonConvert.DeserializeObject<Settings>(settings_str);

מטמון – Cache

מה זה? אחסון מידע או תוצאת חישוב במיקום בו בקשות עתידיות למידע יבוצעו מהר יותר

מתי משתמשים? לרוב כאשר רוצים להאיץ את פעולת התוכנית

הסבר נוסף

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

החיסרון של השיטה הוא כמובן הצורך בשטח אחסון נוסף ובמנגנון שיכתוב ויקרא ממנו.

קיימים מנגנוני cache רבים, הנבדלים אחד מהשני בעיקר במיקום בו נשמר המידע. דוגמאות נפוצות הן: זיכרון התוכנה, קבצים בדיסק, בסיס נתונים, ואפילו שרת ייעודי. חלק מהמנגנונים גם מספקים פיצ'רים שונים, כגון: שמירת מידע לזמן מוגבל (TTL), הגבלת גודל המטמון הכולל (למשל ע"י פינוי LRU) וניהול גרסאות של  המידע.

לדוגמה, שפת python (החל מגרסה 3.2) מציעה מנגנון מובנה לשמירת תוצאות של פונקציות במטמון:

from functools import lru_cache

@lru_cache
def time_consuming_func():
    # time consuming operation

בפעם הראשונה שהפונקציה תיקרא, הפעולה תבוצע במלואה והתוצאה תישמר במטמון (בזיכרון התכנית). החל מהפעם השנייה, הפונקציה לא באמת תרוץ, ובמקום זאת תוחזר התוצאה מהמטמון.

6 thoughts on “"חגורת הכלים" שכל מפתח צריך – חלק א'”

  1. כלים חובה למי שלא מכיר. בלי קשר לכלים שסקרת פה אני מאוד מתחבר לפתיח של המאמר. מפתחים שלומדים להכיר "אבני בניין" קיימים ומסגלים חשיבה של לייצר "אבני בניין" בתוך הפרויקט מקבלים קוד איכותי יותר, קל יותר לתחזוקה וקל לשימוש חוזר.

  2. המנגנון של ה cache בפייתון שנותן לך מובנה על כל פונקציה נשמע אדיר !!
    יש מצב שקיים כזאת חיה ב .net ?

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *