Smart-World Surf

יחידה 3: מבני נתונים בסיסיים

ארגון נתונים עם רשימות, טאפלים ומחרוזות.
רשימות (Lists)טאפלים (Tuples)מחרוזות (Strings)אינדקסים ופרוסות (Slicing)

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

מבוא למבנים סדרתיים (Sequences)

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

מבנה סדרתי (Sequence): אוסף של איברים המסודרים בסדר מסוים, כאשר לכל איבר יש מיקום (אינדקס) ייחודי.

רשימות (Lists)

רשימות הן מבנה הנתונים הגמיש והנפוץ ביותר בפייתון. הן מאפשרות לאחסן אוסף של איברים, שיכולים להיות מטיפוסים שונים (הטרוגניים), והן ניתנות לשינוי (mutable).

  • יצירה: רשימות נוצרות באמצעות סוגריים מרובעים `[]`, כאשר האיברים מופרדים בפסיקים. דוגמה: `my_list = [1, "hello", 3.14]`.
  • יכולת שינוי (Mutability): ניתן להוסיף, להסיר, לשנות או לסדר מחדש איברים ברשימה לאחר יצירתה.
  • פעולות נפוצות:
    • `append()`: הוספת איבר לסוף הרשימה.
    • `extend()`: הוספת איברים מרשימה אחרת.
    • `insert()`: הוספת איבר במיקום ספציפי.
    • `remove()`: הסרת האיבר הראשון בעל ערך מסוים.
    • `pop()`: הסרת איבר לפי אינדקס (ומחזירה אותו).
    • `del`: מחיקת איבר או פרוסה של איברים.
    • `sort()` / `sorted()`: מיון הרשימה.
רשימה (List): מבנה נתונים סדרתי, ניתן לשינוי (mutable), שיכול להכיל איברים מטיפוסים שונים.

טאפלים (Tuples)

טאפלים דומים לרשימות בכך שהם מאחסנים אוסף סדרתי של איברים, אך ההבדל המהותי הוא שהם בלתי ניתנים לשינוי (immutable).

  • יצירה: טאפלים נוצרים באמצעות סוגריים עגולים `()`, כאשר האיברים מופרדים בפסיקים. דוגמה: `my_tuple = (1, "hello", 3.14)`. טאפל עם איבר יחיד דורש פסיק: `(42,)`.
  • אי-יכולת שינוי (Immutability): לאחר יצירת טאפל, לא ניתן לשנות, להוסיף או להסיר ממנו איברים.
  • שימושים:
    • החזרת מספר ערכים מפונקציה.
    • שימוש כמפתחות במילונים (dict keys), מכיוון שהם בלתי ניתנים לשינוי.
    • הבטחת שלמות נתונים כאשר הסדר והערכים קבועים.
טאפל (Tuple): מבנה נתונים סדרתי, בלתי ניתן לשינוי (immutable), שיכול להכיל איברים מטיפוסים שונים.

מחרוזות (Strings)

מחרוזות הן למעשה סדרות של תווים. כמו טאפלים, גם מחרוזות הן בלתי ניתנות לשינוי (immutable).

  • יצירה: מחרוזות נוצרות באמצעות גרשיים בודדים `''` או כפולים `""`. דוגמה: `my_string = "Hello Python"`.
  • אי-יכולת שינוי (Immutability): לא ניתן לשנות תו בודד במחרוזת לאחר יצירתה. כל פעולה שנראית כשינוי מחרוזת, למעשה יוצרת מחרוזת חדשה.
  • פעולות נפוצות:
    • שרשור (`+`): חיבור מחרוזות.
    • חזרה (`*`): חזרה על מחרוזת מספר פעמים.
    • `len()`: אורך המחרוזת.
    • מתודות מובנות רבות: `upper()`, `lower()`, `strip()`, `split()`, `join()`, `find()`, `replace()` ועוד.
מחרוזת (String): מבנה נתונים סדרתי, בלתי ניתן לשינוי (immutable), המכיל סדרה של תווים.

אינדקסים ופרוסות (Indexing and Slicing)

גישה לאיברים בודדים או לתת-סדרות של איברים במבנים סדרתיים מתבצעת באמצעות אינדקסים ופרוסות.

  • אינדקסים (Indexing):
    • אינדקסים מתחילים מ-0 עבור האיבר הראשון.
    • ניתן להשתמש גם באינדקסים שליליים: `-1` מתייחס לאיבר האחרון, `-2` לשני מהסוף וכן הלאה.
    • דוגמה: `my_list[0]` ייתן את האיבר הראשון. `my_string[-1]` ייתן את התו האחרון.
  • פרוסות (Slicing):
    • מאפשרות לחלץ תת-סדרה של איברים. התחביר הוא `[start:end:step]`.
    • `start`: האינדקס ההתחלתי (כולל). ברירת מחדל 0.
    • `end`: האינדקס הסופי (לא כולל). ברירת מחדל סוף הסדרה.
    • `step`: גודל הדילוג בין האיברים. ברירת מחדל 1.
    • דוגמה: `my_list[1:4]` ייתן את האיברים מאינדקס 1 עד 3. `my_string[::2]` ייתן כל תו שני.
    • חשוב לזכור: פעולת חיתוך תמיד מחזירה אובייקט חדש מאותו סוג.
אינדקס (Index): מספר שלם המציין את מיקומו של איבר במבנה סדרתי.
פרוסה (Slice): תת-סדרה של איברים הנבחרת ממבנה סדרתי באמצעות טווח אינדקסים.
יכולת שינוי (Mutability) ואי-יכולת שינוי (Immutability): הבנה עמוקה של ההבדל בין מבנים ניתנים לשינוי (כמו רשימות) לבין מבנים בלתי ניתנים לשינוי (כמו טאפלים ומחרוזות) היא קריטית במבחן ובפרקטיקה. מבנים ניתנים לשינוי יכולים להוביל לתופעות לוואי לא צפויות (side effects) כאשר מספר משתנים מצביעים על אותו אובייקט בזיכרון. לדוגמה, שינוי רשימה דרך משתנה אחד ישפיע על כל המשתנים האחרים המצביעים לאותה רשימה. לעומת זאת, מבנים בלתי ניתנים לשינוי מבטיחים שברגע שנוצרו, ערכם לא ישתנה, מה שמקל על מעקב אחר מצב התוכנית ומונע באגים מסוימים. כמו כן, רק אובייקטים בלתי ניתנים לשינוי יכולים לשמש כמפתחות במילונים.

השוואה ובחירת מבנה הנתונים הנכון

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

רשימות (Lists)

מאפיינים: סדרתי, ניתן לשינוי, הטרוגני. נוצר עם `[]`.

מתי להשתמש: כאשר יש צורך באוסף של איברים שצפוי להשתנות (הוספה, הסרה, שינוי). לדוגמה, רשימת קניות, נתוני משתמשים שניתן לעדכן.

טאפלים (Tuples)

מאפיינים: סדרתי, בלתי ניתן לשינוי, הטרוגני. נוצר עם `()`.

מתי להשתמש: כאשר יש צורך באוסף קבוע של איברים שלא ישתנה. לדוגמה, קואורדינטות (x,y), פרטי תאריך (שנה, חודש, יום), החזרת מספר ערכים מפונקציה.

מחרוזות (Strings)

מאפיינים: סדרתי, בלתי ניתן לשינוי, אוסף של תווים. נוצר עם `''` או `""`.

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

שאלות לדיון

  • הסבירו את ההבדל המהותי בין רשימה לטאפל, ותנו דוגמה לתרחיש שבו תעדיפו להשתמש בטאפל על פני רשימה.
  • כיצד אי-יכולת השינוי של מחרוזות משפיעה על האופן שבו אנו מבצעים עליהן פעולות כמו שינוי תו בודד או שרשור?
  • בהינתן הרשימה `data = [10, 20, 30, 40, 50]`, מה יהיה הפלט של הפעולות הבאות: `data[1:4]`, `data[:3]`, `data[::2]`, `data[::-1]`? הסבירו כל מקרה.
  • תארו מצב שבו שימוש לא נכון ברשימות (בגלל יכולת השינוי שלהן) עלול להוביל לבאגים קשים לאיתור בתוכנית.

נקודות לתשובת מודל

  • רשימה vs. טאפל: רשימה ניתנת לשינוי (mutable), טאפל בלתי ניתן לשינוי (immutable). טאפל עדיף לנתונים קבועים, למשל, ייצוג נקודה במישור `(x, y)` או פרטי תאריך, כדי להבטיח את שלמות הנתונים ולמנוע שינויים בלתי רצויים.
  • השפעת אי-יכולת שינוי על מחרוזות: לא ניתן לשנות תו בודד במחרוזת קיימת. כל פעולה שנראית כשינוי (כמו שרשור או החלפת תו), למעשה יוצרת מחרוזת חדשה בזיכרון. המחרוזת המקורית נשארת ללא שינוי.
  • פלט פעולות Slicing:
    • `data[1:4]` -> `[20, 30, 40]` (אינדקס 1 עד 3).
    • `data[:3]` -> `[10, 20, 30]` (מההתחלה עד אינדקס 2).
    • `data[::2]` -> `[10, 30, 50]` (כל איבר שני).
    • `data[::-1]` -> `[50, 40, 30, 20, 10]` (היפוך הרשימה).
  • באגים מ-Mutability: אם שני משתנים מצביעים על אותה רשימה, ומשתנה אחד משנה את הרשימה, השינוי ישתקף גם במשתנה השני. לדוגמה, אם `a = [1, 2]` ו-`b = a`, ואז `b.append(3)`, גם `a` וגם `b` יהיו `[1, 2, 3]`. זה יכול לגרום לבאגים כאשר מצפים ש-`a` יישאר ללא שינוי.
מצאתם טעות או שחסר משהו?
→ הקודמת
בקרת זרימה ופונקציות
הבאה ←
מבני נתונים מתקדמים