פונקציות מובנות

הקדמה

אנו פוגשים הרבה בעיות בתכנות לעיתים קרובות ובמגוון מצבים:

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

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

השם הנפוץ עבור הפונקציות הללו הוא builtins, ואפשר למצוא את התיעוד של כולן כאן.

חשבו על פונקציות כאלו שאתם כבר מכירים משיעורים קודמים.
חלק מהפתרונות האפשריים: len, int, float, str, list, type

חשוב!
פתרו לפני שתמשיכו!

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

מתמטיקה

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

ערך מוחלט

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


In [ ]:
print(abs(-5))

In [ ]:
numbers = [5, -5, 1.337, -1.337]
for number in numbers:
    print(f"abs({number:>6}) = {abs(number)}")

המשמעות של :>6 ב־fstring היא "דאג שיהיו לפחות 6 תווים, וישר את הערך לימין".
אפשר להחליף את > בתו ^ לצורך יישור לאמצע, או בתו < לצורך יישור לשמאל.

מקסימום ומינימום

הפונקציות max ו־min מקבלות iterable, ומחזירות את האיבר הגבוה או הנמוך ביותר ב־iterable (בהתאמה).


In [ ]:
numbers = [6, 7, 3, 4, 5]
words = ['apple', 'ginger', 'tomato', 'sushi']
print(max(numbers))

In [ ]:
print(f"max(numbers) = {max(numbers)}")
print(f"min(numbers) = {min(numbers)}")
print(f"max(words) = {max(words)}")
print(f"min(words) = {min(words)}")

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


In [ ]:
words.append('ZEBRA')
print(f"Minimum in {words} is {min(words)}??")

זה קורה כיוון שהערכים המספריים של אותיות גדולות קטנים מאלו של אותיות קטנות.
הערך המספרי של Z במילה ZEBRA הוא 90, והערך המספרי של a (במילה apple, שציפינו שתיחשב כקטנה ביותר) הוא 97.

סכום

אפשר לחשב את סכום האיברים ב־iterable באמצעות הפונקציה sum:


In [ ]:
numbers = [1, 1, 2, 3, 5, 8]
sum(numbers)

In [ ]:
i = 1
while i <= len(numbers):
    sum_until_i = sum(numbers[:i])
    print(f"The sum of the first {i} items in 'numbers' is {sum_until_i}")
    i = i + 1

עיגול

אפשר גם לעגל מספרים בעזרת הפונקציה round:


In [ ]:
number = 5.9
round(number)

In [ ]:
numbers = [6, 3.1415, 0.9, -0.9, 0.5, -0.5]
for number in numbers:
    print(f"round({number:>6}) = {round(number)}")

ואפשר להחליט על מספר הספרות אחרי הנקודה שלפיו העיגול יתבצע:


In [ ]:
pi = 3.141592653589793
round(pi, 3)  # הפרמטר השני פה, 3, מייצג את הדיוק

In [ ]:
numbers = [6, 3.1415, 0.9, -0.9, 0.5, -0.5]
round_options = [-1, 1, 2, 3]
for number in numbers:
    for round_argument in round_options:
        result = round(number, round_argument)
        print(f"round({number:>6}, {round_argument}) = {result}")

המרות

טוב, על המרות (casting) כבר למדנו.
אבל בואו בכל זאת נראה כמה דוגמאות מגניבות.

אפשר להמיר מחרוזת לרשימה. הפעולה הזו תפריד כל אחת מהאותיות לתא נפרד ברשימה:


In [ ]:
list('hello')  # Iterable -> List

עוד פרט טריוויה נחמד הוא שלכל ערך בפייתון יש ערך בוליאני שקול.
בדרך כלל, ערכים ריקים שקולים ל־False וערכים שאינם ריקים שקולים ל־True:


In [ ]:
bool_checks = [
    'hello', '', 0, 1, -1, 0.0, 0.1, 1000, '\n', ' ', [], {}, [1],
]

for check in bool_checks:
    print(f"bool({check!r:>7}) is {bool(check)}")

המשמעות של !r ב־fstring היא "הצג את הערך בתצורתו הגולמית" (raw).
לדוגמה, מחרוזות יוצגו עם הגרשיים משני צידיהן.

אפשר להמיר ערכים בוליאניים למספר שלם:


In [ ]:
print(int(True))
print(int(False))

מה בנוגע לטריקים על מילונים?
אם יש לנו iterable שמכיל זוגות של ערכים, אפשר להפוך אותו למילון:


In [ ]:
stock = [('apples', 2), ('banana', 3), ('crembo', 4)]
print(dict(stock))

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


In [ ]:
print(int(5.5))
print(float('5.5'))
print(str(True))
print(bool(0))
print(dict([('name', 'Yam'), ('age', 27)]))
tuple_until_4 = (1, 2, 3, 4)
print(list(tuple_until_4))
print(tuple('yo!'))

אבל צריך לזכור שלא כל טיפוס אפשר להמיר לטיפוס אחר:


In [ ]:
print(list(5))

מאחורי הקלעים של פייתון

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

id

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


In [ ]:
values = ['1337', [1, 3, 3, 7], 1337, ['1', '3', '3', '7']]

for value in values:
    id_of_value = id(value)
    print(f'id({str(value):>20}) -> {id_of_value}')

אם ניצור מצב שבו 2 שמות מצביעים על אותו ערך בדיוק, id תחזיר את אותה התוצאה עבור שני הערכים:


In [ ]:
song1 = ["I've", 'got', 'a', 'lovely', 'bunch', 'of', 'coconuts']
song2 = song1  # שתי הרשימות כרגע מצביעות לאותו מקום
song3 = song1[:]  # הערך זהה, אך שכפלנו את הרשימה כך שהמשתנה הזה יצביע למקום אחר
print(f"id(song1): {id(song1)}")
print(f"id(song2): {id(song2)}  # Same id!")
print(f"id(song3): {id(song3)}  # Another id!")
# נשים לב שהן מתנהגות בהתאם:
song2.append("🎵")
print(f"song1: {song1}")
print(f"song2: {song2}")
print(f"song3: {song3}")

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


In [ ]:
collections_of_numbers = [[0, 0, 0]] * 3  # ניצור 3 רשימות של 3 איברים בכל אחת
print(collections_of_numbers)
collections_of_numbers[0].append(100)  # נוסיף את האיבר 100 לרשימה הראשונה בלבד
print(collections_of_numbers)

בדיקה בעזרת id תעזור לנו לראות שהרשימות הפנימיות בתוך collections_of_numbers מצביעות לאותו מקום:


In [ ]:
print(id(collections_of_numbers[0]))
print(id(collections_of_numbers[1]))
print(id(collections_of_numbers[2]))

נסו לכתוב קוד של שורה אחת או יותר, שיחליף את השורה הראשונה בקוד שלמעלה.
גרמו לכך שהקוד שמוסיף את הערך 100 לרשימה הראשונה לא ישפיע על שאר הרשימות.

חשוב!
פתרו לפני שתמשיכו!

dir

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


In [ ]:
dir('hello')

או שנוכל להעביר לה ממש שם של טיפוס:


In [ ]:
dir(str)

eval

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

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


In [ ]:
x = []
eval("x.append('So lonely')")
print(x)

לעיתים קרובות הפיתוי להשתמש ב־eval הוא גדול.
אפשר לתכנת, לדוגמה, את המחשבון מהתרגול בשבוע השני בצורה הפשוטה הבאה:


In [ ]:
# לנסות בבית עם כפפות – בסדר. לא לכתוב בשום קוד אחר
print(eval(input("Please enter any mathematical expression: ")))

אבל כשחושבים על זה – המשתמש שמתבקש להכניס תרגיל מתמטי, יכול להריץ כל קוד שיתחשק לו, והקוד הזה יבוצע!
הוא יכול לקרוא קבצים שנמצאים על מחשב או למחוק אותם, לחטט בסיסמאות, ולמעשה – לעשות ככל העולה על רוחו.
לקריאה נוספת על הסכנות, חפשו על code injection.

כלים שימושיים נוספים

טווח מספרים

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


In [ ]:
max_number = int(input())

current_number = 0
total = 0

while current_number <= max_number:
    total = total + current_number
    current_number = current_number + 1

print(total)

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


In [ ]:
for i in range(5):
    print(i)

הפונקציה יודעת גם לקבל ערך שממנו היא תתחיל לספור:


In [ ]:
for i in range(12, 14):
    print(i)

וגם ערך דילוג, שקובע על כמה מספרים range תדלג בכל פעם:


In [ ]:
for i in range(0, 101, 10):
    print(i)

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

כתבו בעצמכם קוד שמחשב את סכום המספרים מ־0 ועד המספר שהתקבל כקלט.
השתמשו ב־range.

חשוב!
פתרו לפני שתמשיכו!

סידור

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


In [ ]:
sorted('spoilage')

sorted מקבלת iterable, ומחזירה רשימה מסודרת של האיברים ב־iterable.

נראה דוגמה נוספת של סידור tuple.
שימו לב שטיפוס הנתונים שמוחזר מ־sorted הוא תמיד רשימה:


In [ ]:
numbers = (612314, 4113, 1, 11, 31)
sorted(numbers)

גם ל־sorted וגם לפעולה sort שיכולה להתבצע על רשימות, יש 2 פרמטרים שלא למדנו עליהם.
לפרמטר הראשון קוראים reverse, והוא הפשוט להבנה מבין השניים – ברגע שמועבר אליו True, הוא מחזיר את הרשימה מסודרת בסדר יורד:


In [ ]:
sorted('deflow', reverse=True)

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


In [ ]:
staff = ["Dafi", "Efrat", "Ido", "Itamar", "Yam"]
0 1 2 3 4
"Dafi" "Efrat" "Ido" "Itamar" "Yam"
-5 -4 -3 -2 -1

אם ארצה לסדר את הרשימה הזו לפי האורך (len) של שמות כל אחד מאנשי הסגל, אשתמש ב־key בצורה הבאה:


In [ ]:
staff = sorted(staff, key=len)
print(staff)

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

0 1 2 3 4
"Dafi" "Efrat" "Ido" "Itamar" "Yam"
4 5 3 6 3

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

0 1 2 3 4
"Dafi" "Efrat" "Ido" "Itamar" "Yam"
4 5 3 6 3
0 1 2 3 4
"Ido" "Dafi" "Efrat" "Itamar" "Yam"
3 4 5 6 3

הרשימה המשנית עדיין לא מסודרת. נמשיך:

0 1 2 3 4
"Ido" "Dafi" "Efrat" "Itamar" "Yam"
3 4 5 6 3
0 1 2 3 4
"Ido" "Yam" "Dafi" "Efrat" "Itamar"
3 3 4 5 6

עכשיו הרשימה המשנית מסודרת, ואפשר להפסיק את פעולת הסידור.
הקסם הוא שאפשר להכניס ל־key כל פונקציה, ולקבל iterable מסודר לפי ערכי ההחזרה של אותה פונקציה עבור הערכים ב־iterable.

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

חשוב!
פתרו לפני שתמשיכו!

צימוד ערכים

הרבה פעמים נגיע למצב שבו יש לנו 2 iterables ואנחנו מעוניינים לעבור על הערכים שלהם, זה לצד זה.
נניח שיש לנו רשימה של ציורים, ורשימה של הציירים שציירו אותם:


In [ ]:
paintings = ['Mona Lisa', 'The Creation of Adam', 'The Scream', 'The Starry Night']
artists = ['Leonardo da Vinci', 'Michelangelo', 'Edvard Munch', 'Vincent van Gogh']

צורה אחת לעבור על שתי הרשימות תהיה כזו:


In [ ]:
i = 0
max_iterations = len(paintings)

while i < max_iterations:
    artist = artists[i]
    painting = paintings[i]
    print(f"{artist} painted '{painting}'.")
    i = i + 1

אבל אפשר להסכים על כך שהדרך הזו לא נוחה במיוחד.
אפשרות אחרת היא להשתמש ב־zip, שיצמיד בין שני הערכים ויצור לנו את המבנה הבא:


In [ ]:
zipped_values = zip(paintings, artists)
print(list(zipped_values))

אם שתי הרשימות אינן זהות באורכן, zip לא יתייחס לאיברים העודפים של הרשימה הארוכה יותר.

אחד השימושים האפשריים ל־zip הוא unpacking בתוך לולאת for:


In [ ]:
for artist, painting in zip(artists, paintings):
    print(f"{artist} painted '{painting}'.")

והיא אפילו לא מוגבלת ל־2 ארגומנטים בלבד:


In [ ]:
paintings = ['Mona Lisa', 'The Creation of Adam', 'The Scream', 'The Starry Night']
artists = ['Leonardo da Vinci', 'Michelangelo', 'Edvard Munch', 'Vincent van Gogh']
years = [1503, 1512, 1893, 1889]

for artist, painting, year in zip(artists, paintings, years):
    print(f"{artist} painted '{painting}' in {year}.")

יש טיפוס נתונים שהיה מתאים יותר למקרה הזה מאשר 2 רשימות. מהו לדעתכם?

חשוב!
פתרו לפני שתמשיכו!

שימוש נפוץ ל־zip הוא במקרה שיש לנו שתי רשימות שמתאימות זו לזו, ויחד יכולות ליצור מילון.
אם רשימה אחת מתאימה להיות המפתחות במילון, והאחרת להיות הערכים באותו מילון, נוכל להשתמש ב־zip כדי לבצע את ההמרה:


In [ ]:
paintings = ['Mona Lisa', 'The Creation of Adam', 'The Scream', 'The Starry Night']
artists = ['Leonardo da Vinci', 'Michelangelo', 'Edvard Munch', 'Vincent van Gogh']

artists_from_paintings = dict(zip(paintings, artists))

עכשיו נוכל לשאול מי צייר את המונה ליזה:


In [ ]:
print(artists_from_paintings.get('Mona Lisa'))

או לעבור על המילון ולהדפיס את הערכים, כמו שעשינו לפני כן:


In [ ]:
for painting, artist in artists_from_paintings.items():
    print(f"{artist} painted '{painting}'.")

המרת תווים למספרים ולהפך

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


In [ ]:
print(ord('A'))

In [ ]:
chars = 'a !א'
for char in chars:
    print(f"ord({char!r}) = {ord(char)}")

ויש גם דרך לעשות את הפעולה ההפוכה!
בעזרת הפונקציה chr נוכל לקבל את התו לפי המספר שמייצג אותו:


In [ ]:
ascii_numbers = [97, 32, 33, 1488]
for ascii_number in ascii_numbers:
    # המקפים שם כדי שתראו את הרווח :)
    print(f"chr({ascii_number:>4}) = -{chr(ascii_number)}-")

כתבו תוכנית שמדפיסה זוג ערכים עבור כל מספר מ־9,760 ועד 10,100.
הערך הראשון יהיה המספר, והערך השני יהיה התו שאותו המספר מייצג.

חשוב!
פתרו לפני שתמשיכו!

מניית איברים

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


In [ ]:
with open('resources/haiku.txt') as haiku:
    haiku_text = haiku.readlines()

In [ ]:
line_number = 0
for line in haiku_text:
    print(f"{line_number}:\t{line.rstrip()}")
    line_number = line_number + 1

חייבת להיות דרך טובה יותר!
הפונקציה המובנית enumerate מאפשרת למתכנת להצמיד מספר רץ ל־iterable:


In [ ]:
haiku_text_enumerated = enumerate(haiku_text)
print(list(haiku_text_enumerated))

השימוש הנפוץ ביותר ל־enumerate הוא בלולאות for:


In [ ]:
for line_number, line in enumerate(haiku_text):
    print(f"{line_number}:\t{line.rstrip()}")
    line_number = line_number + 1

טריק מדליק: אם בא לנו להתחיל למספר ממספר שהוא לא 0, אפשר להעביר ל־enumerate את המספר הזה כפרמטר:


In [ ]:
for line_number, line in enumerate(haiku_text, 1):
    print(f"{line_number}:\t{line.rstrip()}")
    line_number = line_number + 1

דוגמאות לשימושים

מפת צופן קיסר

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


In [ ]:
def create_chars_from_numbers(numbers):
    chars = []
    for number in numbers:
        chars.append(chr(number))
    return chars


def get_all_english_letters():
    first_letter = ord('a')
    last_letter = ord('z')
    all_letters_by_number = range(first_letter, last_letter + 1)
    all_letters = create_chars_from_numbers(all_letters_by_number)
    return all_letters


def get_ceaser_map():
    letters = get_all_english_letters()
    shifted_letters = letters[3:] + letters[:3]
    return dict(zip(letters, shifted_letters))

get_ceaser_map()

ורק בשביל הכיף, נבנה קוד שיאפשר לנו להשתמש במפה כדי להצפין מסרים:


In [ ]:
def encrypt(message, encryption_map):
    encrypted = ''
    for char in message.lower():
        # If we can't find the character, assume it is not encrypted.
        encrypted = encrypted + encryption_map.get(char, char)
    return encrypted

encryption_map = get_ceaser_map()
encrypted_message = encrypt('This is the encrypted message!', encryption_map)
print(encrypted_message)

In [ ]:
def create_decryption_map(encryption_map):
    """Actually just flip the keys and the values of the dictionary"""
    decryption_map = {}
    for key, value in encryption_map.items():
        decryption_map[value] = key
    return decryption_map

def decrypt(message, decryption_map):
    decrypted = ''
    for char in message.lower():
        # If we can't find the character, assume it is not encrypted.
        decrypted = decrypted + decryption_map.get(char, char)
    return decrypted

decryption_map = create_decryption_map(encryption_map)
decrypted_message = decrypt(encrypted_message, decryption_map)
print(decrypted_message)

סכימה מדורגת

נבנה תוכנה קצרה שמאפשרת למשתמש להזין מספרים, מסדרת אותם בסדר הפוך, ומדפיסה עבור כל איבר את סכום האיברים עד המיקום של אותו איבר:


In [ ]:
def convert_to_integers(numbers):
    integers = []
    for number in numbers:
        integers.append(int(number))
    
    return integers


numbers = input("Please enter numbers splitted by ',': ").split(',')
integers = convert_to_integers(numbers)
integers.sort(reverse=True)

for i, number in enumerate(integers):
    current_sum = sum(integers[:i+1])
    print(f"The sum until {number} is {current_sum}")

ממוצע ציונים

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


In [ ]:
def get_grades(student_name):
    grades = []
    grade = input(f'Please enter a grade for {student_name}: ')

    while grade.isdecimal():
        grades.append(int(grade))
        grade = input(f'Please enter another grade for {student_name}: ')

    return grades


def get_students():
    students = []
    student = input('Please enter a student: ')

    while student != '':
        students.append(student)
        student = input('Please enter another student: ')

    return students


def get_students_and_grades():
    grades = []
    students = get_students()
    for student in students:
        student_grades = get_grades(student)
        grades.append(student_grades)

    return zip(students, grades)


def get_average_grade(student_and_his_grades):
    grades = student_and_his_grades[1]
    if len(grades) == 0:
        return 0

    return sum(grades) / len(grades)


students_and_grades = get_students_and_grades()
students_sorted_by_grades = sorted(  # מפצלים שורה כדי שלא יהיו שורות ארוכות מדי
    students_and_grades, key=get_average_grade, reverse=True
)
best_student_name, best_grade = students_sorted_by_grades[0]
print(f"The best student is {best_student_name} with the grades: {best_grade}")

תרגילים

אותיות או לא להיות

כתבו תוכנה שמדפיסה את המספר הסידורי של כל אות באלף־בית האנגלי, מהסוף להתחלה.
השתמשו בכמה שיותר פונקציות מובנות בדרך.

הפלט אמור להיראות כך:

26 -> z 25 -> y 24 -> x ... 2 -> b 1 -> a

אנחנו הצלחנו להשתמש ב־5 פונקציות שנלמדו במחברת הזו, בקוד שאורכו 2 שורות.

מלחמה וזהו

זהו תרגיל ברמת קושי גבוהה, שמערב נושאים רבים.
הרגישו בנוח להיעזר במתרגלים שלכם.

מצאו את עשר המילים הנפוצות ביותר בספר "מלחמה ושלום", והדפיסו אותן למסך מהמילה הנפוצה ביותר למילה הנפוצה הכי פחות.
ליד כל מילה הדפיסו את מספר המופעים שלה בספר.
הספר נמצא בקובץ war-and-peace.txt בתוך התיקייה resources.