Rust/פעולות אריתמטיות

מתוך ויקיספר, אוסף הספרים והמדריכים החופשי
Rust

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

הפעולות הבסיסיות[עריכה]

שפת ראסט מאפשרת לחבר (ע"י +), לחסר (ע"י -), להכפיל (ע"י *), לחלק (ע"י /), ולמצוא שארית (ע"י % - מודולו). בדומה לסימן פעולת ההשמה ('='), גם סימני פעולות החשבון מכונים אופרטורים. להלן מספר דוגמאות:

let x = 4;
let y = 2;

// Prints 4 + 2 = 6
println!("{} + {} = {}\n", x, y, x + y);

// Prints 4 - 2 = 2
println!("{} - {} = {}\n", x, y, x - y);

// Prints 4 * 2 = 8
println!("{} * {} = {}\n", x, y, x * y);

// Prints 4 / 2 = 2
println!("{} / {} = {}\n", x, y, x / y);

// Prints 4 % 2 = 0
println!("{} % {} = {}\n", x, y, x % y);

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

let x = 2;
let y = 3;

// Prints 13 
println!("{}\n", x + y + 3 + 5);

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

סדר פעולות החשבון[עריכה]

סדר פעולות החשבון בשפת ראסט הוא המקובל באלגברה בסיסית, ולכן כפל (*), לדוגמה, מבוצע לפני חיבור (+). השורה הבאה, לדוגמה, תדפיס 17:

println!("{}\n", 2 + 3 * 5);

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

println!("{}\n", (2 + 3) * 5);

השמת ערכים[עריכה]

השמה ואתחול[עריכה]

כפי שראינו בפרק על משתנים, ניתן להשתמש בסימן '=' להשמה ואתחול של משתנים.

let x = 7;
let y = x + 3;

חשוב להבין מה קורה כאן בשורה השנייה. מאחר שהמשתנה x הוגדר לפני המשתנה y, ניתן להשתמש בערכו של x על מנת להגדיר את y. ראשית מחשבים את הביטוי x + 3 (ערכו כאן 10) ואז משימים ערך זה למשתנה y. אפשר גם להשים למשתנה ערך חדש שתלוי בערכו הקודם במידה והגדרנו את המשתנה בתור mutable. נתבונן בדוגמה הבאה:

let mut x = 5;
x = x + 2;

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

סימני קיצור בהשמה עצמית[עריכה]

x = x + 2;

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

x += 2;

המשמעות כאן זהה לחלוטין: ממחשבים את ערכו של הביטוי x + 2, ומשימים את הערך ל-x.

שפת Rust כוללת סימונים מקוצרים להשמות עצמיות לכל חמש פעולות החשבון הבסיסיות:

// x = x + 1 
x += 1;

// y = y - 3
y -= 3;

// z = z * 8
z *= 8;

// w = w / 4
w /= 4;

// p = p % 2
p %= 2;

כדאי לדעת:

נוסף לקיצורים שהצגנו, בשפות תכנות רבות (ביניהן שפת C) ישנו קיצור שמגדיל או מקטין את ערך המשתנה ב-1 (למשל x++ או x--). הקיצור שימושי במיוחד לשימוש בתכונות מתקדמות יותר של שפות תכנות שטרם עסקנו בהם (כמו לולאות). אף על פי כן, בשפת ראסט אין קיצור של הגדלה או הקטנה עצמית של ערך המשתנה ב-1 ויש להשתמש בביטוי x+=1 או x-=1.

פעולות אריתמטיות על מספרים מטיפוסים שונים[עריכה]

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

println!("{}\n", 3.3 + 4); // error: mismatched types

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

איבוד אפשרי של מידע בעת ביצוע פעולת חילוק[עריכה]

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

println!("{}\n", (7 + 9 + 1) / 5); // prints 3

הדוגמה אמורה להדפיס את תוצאת החילוק של 17 ב-5 שהיא שוות ערך ל-3.4. עם זאת, כשנריץ את הקוד נגלה שהתוצאה שתודפס לפנינו היא המספר 3. אל דאגה! מדובר בטעות רווחת אצל מתכנתים מתחילים. הסיבה לכך שהודפס 3 היא שהקומפיילר נוהג להתייחס הן לביטוי שבמונה והן לביטוי שבמכנה כאל משתנים מטיפוס של מספר שלם ולכן, תוצאת החילוק של שני מספרים שלמים תהיה בהכרח מטיפוס מספר שלם.

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

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

println!("{}\n", (7 + 9 + 1) / 5); // prints 3

println!("{}\n", (7 + 9 + 1) as f64 / 5 as f64); // prints 3.4

println!("{}\n", (7 + 9 + 1) as f64 / 5.0); // prints 3.4

println!("{}\n", (7 as f64 + 9 as f64 + 1 as f64) / 5 as f64); // prints 3.4

println!("{}\n", (7 + 9 + 1) / 5 as f64); // error: mismatched types

println!("{}\n", ((7 + 9 + 1) / 5) as f64); // prints 3

println!("{}\n", (7 + 9 + 1 as f64) / 5 as f64); // error: mismatched types

תרגול[עריכה]

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


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