רשימות: אופרטורים בסיסיים ואינדקסים#
בסרטון זה למדנו איך לעבוד עם רשימות בפייתון. ראינו איך ליצור רשימה חדשה, איך לשכפל אותה או לחבר שתי רשימות יחד, ואיך להשוות בין רשימות שונות. בנוסף, התנסינו בחיפוש ערכים בתוך רשימה ולמדנו כיצד לגשת לאיברים ספציפיים בעזרת אינדקסים. כל הכלים האלה מאפשרים לנו לנהל אוספי נתונים בצורה גמישה ונוחה.
לנוחותכם חלונית עם הקוד שראינו בסרטון. מוזמנים להריץ בעצמכם:
lst1 = [1,5,4]
lst2 = ["hello", "world"]
lst3 = [23, 5.6, -17, "computer"]
lst4 = []
# print(lst3)
# print(len(lst3))
#print([1,2] + [3])
#print(lst1 + [888])
#print(lst1 + [888,999])
#print(lst1 + lst4)
#print([1,2] * 4)
#print(lst3 * 2)
#print([1,2] == [2,1])
#print([1,2] == [1] + [2])
#print(2 in [1,2])
#print(3 in [1,2])
#print("hello" in lst2)
# print(lst3[0])
# print(lst3[1])
# print(lst3[2])
# i = 3
# print(lst3[i])
print(lst3[4])
בחנו את עצמכם
ניזכר בשתי הרשימות אותן ראינו בסרטון:
lst1 = [1, 5, 4]
lst2 = ["hello", "world"]
נגדיר רשימה נוספת:
lst3 = []
שימו לב - הרשימה lst3 היא רשימה ריקה.
בכל שאלה תופיע פקודה מסוימת המערבת את אחת או יותר מהרשימות הנ”ל. עליכם לסמן מהו הערך המוחזר מהפקודה, או לסמן כי הפקודה מביאה לשגיאה.
דוגמאות:
הפקודה
len(lst1)מחזירה 3 - זהו אורך הרשימהlst1.הפקודה
lst3[0]מביאה לשגיאה, שכן הרשימה ריקה ולכן לא ניתן לגשת לאיבר שנמצא באינדקס 0 שלה.
לנוחיותכם, יצירנו חלונית קוד בה תוכלו לבדוק את עצמכם ולהשתכנע בנכונות התשובות. אנו ממליצים לענות תחילה על כל השאלות ולאחר מכן לבדוק את עצמכם בחלונית הקוד.
תזכורת: על מנת להציג את הערך המוחזר של פקודה מסוימת, “עטפו” אותה בפקודת print. למשל: print(len(lst1))
lst1 = [1, 5, 4]
lst2 = ["hello", "world"]
lst3 = []
# check your answers here:
עד כה למדנו שרשימה היא רצף מסודר של איברים. בפייתון יוצרים רשימה על ידי עטיפת האיברים בסוגריים מרובעים ([]) והפרדתם באמצעות פסיקים. חשוב לזכור שאפשר לשים ברשימה איברים מטיפוסים שונים, כלומר מספרים, מחרוזות, ערכים בוליאניים ואף רשימות נוספות – כל אלה יכולים להתקיים באותה רשימה.
my_list = [2, 3, 5, 7, 11]
my_list
[2, 3, 5, 7, 11]
days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
days
['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
pi = ['pi', 3.14159, True]
pi
['pi', 3.14159, True]
גישה לאינדקסים ברשימות#
זוכרים את זה?
אינדקסים וחיתוך (slicing) חלים גם על רשימות בדיוק באותו האופן.
my_list = [2, 3, 5, 7, 11]
print(my_list[0])
print(my_list[4])
print(my_list[-3])
2
11
5
my_list[5]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[20], line 1
----> 1 my_list[5]
IndexError: list index out of range
חיתוך (Slicing)#
גם עם רשימות אפשר לבצע חיתוכים, ממש כפי שעשינו עם מחרוזות. אנחנו משתמשים באותו מבנה מוכר: lst[start:end:step].
כשאנחנו חותכים רשימה, אנחנו בעצם יוצרים ממנה רשימה חדשה, שמכילה את האיברים שבטווח שבחרנו. זה מאפשר לנו לעבוד רק עם חלק מהנתונים, בלי לשנות את הרשימה המקורית.
my_list = [1,2,3,4,5,6,7,8,9,10]
חיתוך מתבצע מאינדקס הstart (כולל) עד אינדקס הend (לא כולל)
print(my_list[1:5]) # slicing
[2, 3, 4, 5]
ניתן להשתמש גם באינדקסים השליליים עבור חיתוך
print(my_list[0:-1]) # forward/backward indexing
[1, 2, 3, 4, 5, 6, 7, 8, 9]
השארת הstart והend ריקים תשתמש בערכים דיפולטיים - תחילת וסוף הרשימה בהתאמה
print(my_list[::2]) # add a step
[1, 3, 5, 7, 9]
print(my_list[::-1]) # reverse
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
כאשר נוצר חיתוך ריק, בו לא נמצאים ערכים, נקבל רשימה ריקה.
print(my_list[3:8:-2]) # output is an empty list. This is NOT an error
[]
כמובן שחיתוך לא משנה את הרשימה המקורית, אלא מייצר רשימה חדשה לחלוטין.
print(my_list) # slicing does NOT change original list!
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
my_list = [1, 2, 3, 4, 5]
new_list = my_list[::2]
print(new_list)
print(my_list)
[1, 3, 5]
[1, 2, 3, 4, 5]
כלומר, ראינו ששליפה של איברים מתוך רשימה דומה מאוד לשליפת תווים ממחרוזת, על-ידי אינדקסים. אבל היתרון הגדול של רשימות על פני מחרוזות הוא שהן mutable - כלומר, ניתנות לשינוי. זה אומר שאפשר לא רק לקרוא ערך שנמצא במקום מסוים ברשימה, אלא גם להחליף אותו בערך חדש. לדוגמה:
lst = [10, 20, 30]
lst[1] = 99
print(lst) # [10, 99, 30]
[10, 99, 30]
כאן השתמשנו באינדקס כדי להגיע למיקום השני ברשימה, ואז הצבנו שם ערך חדש. פעולה כזו אפשרית כי רשימה היא מבנה נתונים הניתן לשינוי, בניגוד למחרוזות שראינו עד כה, שבהן אי-אפשר לשנות תו קיים אלא רק ליצור מחרוזת חדשה.
תרגול#
ממשו את הפונקציה middle_of_list(lst) אשר מקבלת רשימה lst ומחזירה את האיבר במיקום האמצעי שלה.
אם מדובר ברשימה באורך זוגי, הפונקציה תחזיר את השמאלי מבין האיברים האמצעיים.
שימו לב: יש מגוון דרכים לממש את הפונקציה, וניתן לממש זאת באמצעות שורת קוד אחת בלבד.
def middle_of_list(lst):
# Write your code here
pass
לחצו כאן כדי לצפות בפתרון
ראשית, ניזכר שאנחנו יודעים למצוא איבר בהינתן האינדקס שלו על-ידי הפקודה lst[index]. כלומר, אם נמצא את האינדקס הדרוש, נוכל לבצע return lst[index] ובכך לסיים את הפונקציה.
כעת נתמקד בשאלה המעניינת יותר - כיצד למצוא את האינדקס האמצעי לכל אחת מהרשימות?
התשובה לשאלה, כמובן, מסתמכת על אורך הרשימה, ולכן נידרש להשתמש בlen(lst).
נבחן כמה דוגמאות כדי לוודא שאנחנו מבינים איזה אינדקס נרצה לקבל:
אם הרשימה באורך אי-זוגי: אם הרשימה באורך 5, נרצה את אינדקס 2. אם הרשימה באורך 9, נרצה את אינדקס 4. מה המשותף למקרים הללו? מדובר בחצי אורך הרשימה בעיגול כלפי מטה. כלומר במקרים הללו נוכל להשתמש בfloor division על מנת להגיע לאינדקס הרצוי. index = len(lst)//2.
אם הרשימה באורך זוגי: אם הרשימה באורך 6, נרצה את אינדקס 2. אם הרשימה באורך 10, נרצה את אינדקס 4. מה קורה במקרים הללו? פה דווקא נרצה את חצי אורך הרשימה פחות אחת. כלומר index = len(lst)//2 - 1.
אם כך, דרך ראשונה שהיינו יכולים לפתור בה, הייתה על ידי בלוק תנאי פשוט - שבודק אם הרשימה זוגית ומחזיר את האינדקס בהתאם. אך אמרנו שניתן לממש את הפונקציה בשורה אחת? אז מה עוד אפשרי לעשות?
ניתן לנצל את העובדה שהביטוי len(lst) % 2 == 0 מחזיר ערך בוליאני, ובפייתון אפשר להפוך אותו למספר על ידי int(). כך, עבור רשימה זוגית נקבל 1, ועבור רשימה אי־זוגית נקבל 0, ונוכל לחשב את האינדקס האמצעי בשורה אחת: index = len(lst)//2 - int(len(lst)%2 == 0).
אך ניתן להגיע לאינדקס בצורה אפילו יותר קצרה! אם נסתכל שוב על הדוגמאות, נוכל לראות תבנית מעניינת: עבור רשימה באורך אי‑זוגי כמו 9, קיבלנו את האינדקס 4, ועבור רשימה באורך זוגי כמו 10, קיבלנו את אותו אינדקס. באופן דומה, עבור רשימה באורך 5 וגם באורך 4, קיבלנו את האינדקס 2. כלומר, אם נתבונן מקרוב, אפשר לראות שיש דרך לחשב את האינדקס האמצעי לכל המקרים בשורה אחת בלבד, בלי צורך בתנאי: index = (len(lst) - 1) // 2
ולכן, תוכן הפונקציה הסופי שלנו יכול להיות:
def middle_of_list(lst):
return lst[(len(lst)-1)//2)]