הגדרת פונקציות מבוא לתכנות מדעי וסטטיסטי R פונקציות, ו חלק 4 בנוסף לפונקציות שמגיעות מוכנות יחד עם המערכת exp) mean,,c וכו'), אפשר לכתוב פונקציות חדשות פונקציות נקראות לעתים "פרוצדורות" או "סאב-רוטינות" נכתוב פונקציה בשם,mid.range שמקבלת כארגומנט וקטור של מספרים, ומחשבת את אמצעהטווחשל המספרים בווקטור (כלומר את הממוצע של המספר המקסימלי והמינימלי) > mid.range(c(3, 10, 2, 3, 12)) [1] 7 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 2 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 1 הגדרת פונקציות הגדרת פונקציות הפונקציה מוגדרת כך: מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 3 mid.range <- function(x){ y <- mean(range(x)) return(y) בשורה הראשונה מופיעים (בסדר הבא): שם הפונקציה אופרטור ההשמה המילה,function והארגומנט/ םי (בסוגריים, מופרדים ע"י פסיקים) בהמשך מופיע גוף הפונקציה, בין זוג סוגריים מסולסלים ל"תוכנה" שכותבים, כמו פונקציה זו, קוראים "קוד" כדי ש- R תכיר פונקציה שכתבנו, יש לטעון אותה לזיכרון שלוש דרכים לעשות זאת: לכתוב פונקציה ישירות לקונסולה, שורה שורה (מועד לבעיות) לכתוב פונקציה בעורך טקסט,(editor) לקונוסולה (הכי שימושי, לצרכים שלנו) מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 4 להעתיק, ולהדביק עורך טקסט מובנה של R או Notepad R, Studio או Notepad++ לשמור פונקציה בקובץ, ולהריץ אותו (נראה בהמשך) כדי שפונקציה שהגדרנו תישמר בזיכרון לפעם הבאה שנשתמש ב- R, צריך לשמור את סביבת העבודה
כללים לכתיבת פונקציה קריאה הגדרת פונקציות הערך שהפונקציה מחזירה הוא הערך שבתוך ה- return אם לא מופיע,return הפונקציה תחזיר את ערך הביטוי האחרון שמופיע בה את הפונקציה האחרונה היה אפשר לכתוב גם כך: mid.range <- function(x){ y <- mean(range(x)) y מומלץ להשתמש ב- return (בהמשך נבין טוב יותר למה) האם באמת היינו צריכים את המשתנה y? עקרונית, ניתן היה לכתוב את כל הפונקציה בשורה אחת: mid.range <- function(x){y <- mean(range(x)); return(y) זוהי כתיבה מאד לא נוחה לקריאה מומלץ להפריד את הפקודות לשורות לרשום הערות בגוף הפונקציה באמצעות סולמיות (הסימן #) לכתוב פונקציה קצת יותר ארוכה, אם היא יותר קריאה להזיח indent) (to את תחילת השורות שבגוף הפונקציה לכיוון פנים העמוד (כנ"ל ב וב, שנראה בהמשך) לבדוק האם כבר קיימת פונקציה בעלת אותו השם מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 6 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 5 תרגילים תקינות הקלט עקרונית, פונקציה צריכה לבדוק שהקלט שלה "הגיוני", ולתת הודעת שגיאה אם הוא לא > exp("banana") Error in exp("banana") : Non-numeric argument to mathematical function בפונקציות שאנחנו נכתוב, אלא אם כן נאמר אחרת, אפשר להניח שהקלט הגיוני, כלומר שלא קוראים לפונקציה ש"מצפה" למספר חיובי עם ארגומנט שהוא שלילי, או מטריצה, או מחרוזת, וכו' כתבו פונקציה המקבלת כארגומנט מספר טבעי n, ומחזירה מטריצת אפסים ממימד n n כתבו פונקציה המקבלת כארגומנט מטריצה, ומחזירה את סכום ארבעת האיברים שבארבע ה"פינות" שלה.1.2 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 8 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 7
השמות ופונקציות השמות ופונקציות השמות שמתבצעות בתוך הפונקציה (למשתנה y, בדוגמא שלנו) לא מוכרות מחוץ לפונקציה > mid.range(c(3, 10, 2, 3, 12)) [1] 7 > y Error: object "y" not found למשתנים שכאלה קוראים משתנים "מקומיים" (local) לפונקציה בניגוד להרבה שפות אחרות, פונקציות ב- R לפעמים כן מכירות משתנים שהוגדרו בקונסולה, גם אם הם לא הועברו כארגומנטים למשל, אם נגדיר פונקציה add.y אז ניתן לקבל בקונסולה מאדלאמומלץלהשתמש בתכונה זו של R add.y <- function(x){ return(x + y) > y <- 5 > add.y(3) [1] 8 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 10 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 9 ארגומנטים עם ערך ברירת מחדל פונקציות עם יותר מארגומנט אחד פונקציה יכולה לקבל שני ארגומנטים או יותר, כשהם מופרדים על-ידי פסיקים למשל, נגדיר פונקציה המקבלת שני מספרים, ומחזירה את הראשון ועוד מחצית השני half.sum <- function(x, y){ return(x + y/2) > half.sum(3, 10) [1] 8 יש פונקציות שלא מקבלות אף ארגומנט (למשל (objects לארגומנט של פונקציה אפשר להגדיר ערך ברירת מחדל,(default) בו הפונקציה תשתמש אם הארגומנט מושמט בקריאה למשל, נגדיר פונקציה,root המחשבת את השורש של מספר נתון; אלא אם כן נאמר אחרת, השורש הוא ריבועי root <- function(x, y=2){ return(x^(1/y)) > root(81); root(81, 2); root(81, 4) [1] 9 [1] 9 [1] 3 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 12 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 11
קריאה לפונקציה קריאה לפונקציה אין לבלבל בין שמות הארגומנטים לפונקציה, כפי שהם מופיעים בהגדרת הפונקציה, לבין שמות המשתנים איתם קוראים לפונקציה > x <- 4 > y <- 12 > half.sum(y, x) [1] 14 כשקוראים לפונקציה, אפשר לשחק עם סדר הארגומנטים, אם נוקבים בשמות שלהם, כפי שהם מתקבלים בתוך הפונקציה כך לא חייבים לזכור את הסדר המדויק של הארגומנטים דוגמא: במדינה כלשהי, שיעור המס הבסיסי הוא בד"כ 30% (אלא אם כן נאמר אחרת), ומקבלים הנחה של 5% במיסוי לכל ילד נכתוב פונקציה המחשבת את המס שצריך לשלם על סכום נתון מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 14 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 13 קריאה לפונקציה מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 15 > compute.tax(1000) [1] 300 > compute.tax(1000, no.children=1) [1] 250 > compute.tax(1000, tax.rate=20) [1] 200 > compute.tax(1000, tax.rate=20, no.children=2) [1] 100 הפונקציה עצמה: compute.tax <- function(x, no.children=0, tax.rate=30){ return(x*(tax.rate - no.children*5)/100) לעתים רוצים לעשות משהו רק אם תנאי מסוים מתקיים לשם כך משתמשים ב- if נממש את הפונקציה abs (ערך מוחלט): my.abs <- function(x){ if(x < 0) x <- -x return(x) מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 16 > my.abs(3) [1] 3 > my.abs(-2) [1] 2
לפעמים רוצים לעשות דבר אחד אם התנאי מתקיים, ודבר אחר, אם לא לשם כך משתמשים ב- else אחרי ה- if למשל, את הפונקציה abs ניתן היה לממש גם כך my.abs <- function(x){ if(x < 0) return(-x) else return(x) אם הביטויים שאחרי ה- if או ה- else נמשכים על פני יותר משורה אחת, צריך לתחום אותם בין סוגריים מסולסלים נגדיר פונקציה שמחשבת שטח של מלבנים ומשולשים > compute.area("rectangle", 3, 4) [1] 12 > compute.area("triangle", 3, 4) [1] 6 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 18 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 17 הפונקציה מוגדרת כך: compute.area <- function(shape, x, y){ if(shape == "rectangle"){ area <- x*y return(area) else{ area <- x*y/2 return(area) האם באמת היינו צריכים שתי פקודות אחרי כל מקרה? מהי התוצאה של compute.area("banana",3,4)? אפשר להשתמש בכמה else ברצף, כדי לטפל ביותר ממקרה אחד למשל, אם רוצים שהפונקציה תדע לטפל גם במקביליות, וגם תחזיר NA אם הצורה המבוקשת אינה "תקנית": compute.area <- function(shape, x, y){ if(shape == "rectangle") area <- x*y else if(shape == "triangle") area <- x*y/2 else if(shape == "parallelogram") area <- x*y else return(na) return(area) מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 20 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 19
ניתן להשתמש בתוך התנאי באופרטורים לוגיים && (שני אמפרסנטים) זה "וגם" (שני קווים אנכיים) זה "או"! (סימן קריאה) זה "לא" ההבדל בין & (שראינו בעבר) לבין && הוא ש-& עובד על וקטורים, בעוד && מיועד לעבוד על ביטוי לוגי בודד > c(t, F) & c(t, F) [1] TRUE FALSE > c(t, F) && c(t, F) [1] TRUE כנ"ל עם לעומת את הפונקציה האחרונה ניתן היה להגדיר כך מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 22 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 21 compute.area <- function(shape, x, y){ if((shape == "rectangle") (shape == "parallelogram")) area <- x*y else if(shape == "triangle") else area <- x*y/2 return(na) return(area) אפשר לגרום לכך שכשהצורה המבוקשת אינה תקנית, הפונקציה תדפיס הודעת שגיאה ותעצור (מבלי להחזיר אף ערך) > compute.area("rectangle", 3, 4) [1] 12 > compute.area("banana", 3, 4) Error in compute.area("banana", 3, 4) : illegal shape כדי לעשות זאת, משתמשים בפונקציה stop compute.area <- function(shape, x, y){ if((shape == "rectangle") (shape == "parallelogram")) area <- x*y else if(shape == "triangle") area <- x*y/2 else stop("illegal shape") return(area) מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 24 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 23
לפעמים רוצים לא לעצור, אבל להודיע ש"משהו כנראה לא בסדר" אם בפונקציה האחרונה אחד הארגומנטים המספריים הוא שלילי, כדאי להתריע על כך בהודעת אזהרה > compute.area("triangle", 3, 4) [1] 6 > compute.area("triangle", 3, -4) [1] -6 Warning message: In compute.area("triangle", 3, -4) : length is negative כדי לעשות זאת, משתמשים בפונקציה warning compute.area <- function(shape, x, y){ if((x < 0) (y < 0)) warning("length is negative") if((shape == "rectangle") (shape == "parallelogram")) area <- x*y else if(shape == "triangle") area <- x*y/2 else stop("illegal shape") return(area) מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 26 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 25 טעות נפוצה היא לכתוב בתוך ה- if ביטוי לוגי וקטורי במקרה זה תופיע הודעת אזהרה, ורק התוצאה הלוגית הראשונה בווקטור תילקח בחשבון > x <- 1:10 > if(x < 5) y <- 17 Warning message: In if (x < 5) y <- 17 : the condition has length > 1 and only the first element will be used הטעות הזו מופיעה לעתים קרובות כששוכחים להשתמש בפונקציות all או any ב- R מוגדרת פונקציה בשם,ifelse עם 3 ארגומנטים הארגומנט הראשון הוא תנאי לוגי הארגומנט השני יוחזר אם התנאי הלוגי הוא TRUE הארגומנט השלישי יוחזר אם התנאי הלוגי הוא FALSE מימוש של הפונקציה max (על שני מספרים) ע"י :ifelse my.max <- function(x, y){ z <- ifelse(x > y, x, y) return(z) מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 28 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 27
הערות כלליות על אפשר למקם תנאי בתוך תנאי אחר (שיכול להיות בתוך תנאי שלישי, וכו') מומלץ להשתמש בהזחות, כדי שיהיה קל יותר לקרוא אם פקודה מסוימת מתבצעת בכל המקרים של התנאי, עדיף לרשום אותה פעם אחת מחוץ להם הרבה פעמים רוצים לבצע רצף פעולות מסוים שוב ושוב הפתרון: לולאה (loop) ב- R יש שלושה סוגי : לולאת for (הכי שימושית) לולאת while לולאת repeat כל "סיבוב" של לולאה נקרא "איטרציה" (iteration) מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 30 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 29 דוגמא: נגדיר פונקציה בשם sum.squares המקבלת כארגומנט מספר טבעי n, ומחזירה את סכום הריבועים 1 2 + 2 2 + + n 2 > sum.squares(3) [1] 14 > sum.squares(5) [1] 55 דרך ראשונה: באמצעות לולאת for sum.squares <- function(n){ result <- 0 for(i in 1:n){ result <- result + i^2 return(result) הקוד שבתוך הלולאה מתבצע שוב ושוב, ובכל איטרציה המשתנה שמשמאל למילה in (המשתנה i, אצלנו) מקבל את הערך הבא בווקטור שמימין ל- in (הווקטור n:1, אצלנו) מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 32 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 31
דרך שנייה: באמצעות לולאת while sum.squares <- function(n){ result <- 0 i <- 1 while(i <= n){ result <- result + i^2 i <- i + 1 return(result) הקוד שבתוך הלולאה מתבצע שוב ושוב, כל עוד הביטוי שבסוגריים שאחרי ה- while הוא TRUE דרך שלישית: באמצעות לולאת repeat sum.squares <- function(n){ result <- 0 i <- 1 repeat{ result <- result + i^2 i <- i + 1 if(i > n) break return(result) הקוד שבתוך הלולאה מתבצע שוב ושוב, ואפשר לצאת מהלולאה רק באמצעות הפקודה break מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 34 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 33 אפשר היה לממש את הפונקציה sum.squares בכלל ללא sum.squares <- function(n){ return(sum((1:n)^2)) תמידעדיף ב- R להימנע מ, אם אפשר, משום שהן עלולות להאט את הריצה באופן משמעותי כמה זמן לוקח לקבל תשובה עבור 10 7 בשתי השיטות? אפילו את הפונקציה האחרונה אפשר לשפר, ע"י הנוסחה באמצעות break אפשר לצאת גם מ for ו- while אם אנחנו בלולאה שבתוך לולאה (שאולי בתוך עוד לולאה, וכו'), אז יוצאים מהלולאה הפנימית ביותר הפקודה next (בתוך לולאה), גורמת לתוכנית לעבור מיד לאיטרציה הבאה של הלולאה, מבלי להשלים את הפקודות שנותרו באיטרציה הנוכחית תכנות עם break ו- next נחשב למגושם, ומומלץ להימנע ממנו מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 36 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 35
תרגילים דוגמא השערת קולאץ כתבו פונקציה המקבלת שני מספרים טבעיים, m ו- n, ומחזירה מטריצה בת m שורות ו- n עמודות, בה השורה הראשונה כולה 1, השנייה כולה 2,... והשורה האחרונה כולה m כתבו פונקציה המקבלת שני מספרים טבעיים, a ו- b, ומוצאת כמה מספרים בין a ו- b מתחלקים ללא שארית ב- 3 אוב- 7 "השערת קולאץ" Conjecture) (Collatz היא בעיה פתוחה במתמטיקה מתחילים ממספר טבעי כלשהו, ומפעילים שוב ושוב פונקציה f, המוגדרת כך אם מתחילים מ- 8, מקבלים 8 4 2 1 4 2 1 אם מתחילים מ- 3 מקבלים 3 10 5 16 8 4 2 1 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 38 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 37 דוגמא השערת קולאץ דוגמא השערת קולאץ בהינתן מספר התחלתי כלשהו, תוך כמה צעדים נגיע ממנו לראשונה ל- 4? נכתוב תוכנית ב- R שתענה על שאלה זו התוכנית בפעולה: האם מכל מספר התחלתי נגיע מתישהו ל"לופ" 4,2,1? אף אחד לא יודע את התשובה לשאלה הזו! מצד אחד, מכל מספר התחלתי שניסו, הגיעו בסופו של דבר ללופ הזה (כלומר לא נמצאה דוגמא נגדית) אבל יש אין-סוף מספרים, כך שאי אפשר לבדוק את כולם מצד שני, אף אחד לא הצליח להוכיח שתמיד מגיעים ללופ מתמטיקאים משערים שהתשובה לשאלה היא "כן" > collatz.steps(3) [1] 5 > collatz.steps(27) [1] 109 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 40 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 39
רקורסיה דוגמא השערת קולאץ f <- function(n){ if(n%%2 == 0) return(n/2) else return(3*n + 1) collatz.steps <- function(n){ steps <- 0 while(n!= 4){ n <- f(n) steps <- steps + 1 return(steps) # n is even # n is odd הפונקציה f: הפונקציה הראשית: פונקציה יכולה לקרוא גם לעצמה; למצב כזה קוראים "רקורסיה" (recursion) למשל, את הפונקציה "עצרת" (factorial) ניתן להגדיר מתמטית באופן רקורסיבי המקרה = 0 n הוא הבסיסשל הרקורסיה בדוגמא מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 42 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 41 רקורסיה רקורסיה fact <- function(n){ if(n == 0) return(1) else return(n*fact(n - 1)) > fact(5) [1] 120 > fact(10) [1] 3628800 ניתן להגדיר ב- R את הפונקציה כך מה נקבל כתשובה לפקודה fact(2.5)? איך ניתן לממש את הפונקציה באופן לא רקורסיבי? עוד דוגמא: נכתוב פונקציה רקורסיבית המקבלת מספר טבעי n, ומחזירה מטריצת יחידה מסדר n > I.mat(3) [,1] [,2] [,3] [1,] 1 0 0 [2,] 0 1 0 [3,] 0 0 1 > I.mat(1) [,1] [1,] 1 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 44 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 43
רקורסיה רקורסיה הרעיון: בתוך מטריצת יחידה מסדר n "מתחבאת" מטריצת יחידה מסדר 1 n, ורק צריך לצרף לה אפסים ואת המספר 1 הפונקציה: I.mat <- function(n){ if(n == 1) return(matrix(1,1,1)) else return(rbind(cbind(1, matrix(0,1,n-1)), cbind(matrix(0,n-1,1), I.mat(n-1)))) מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 46 מבוא לתכנות מדעי וסטטיסטי R חלק 4 סמסטר ב' תשע"ו, 2015/16 45