המדריך השלם לפרויקט תוכנה ב Dr Scheme

‫המדריך השלם לפרויקט תוכנה ב‪Dr Scheme‬‬
‫המדריך מיועד לאנשים שכבר יש להם ניסיון בשפות תכנות סטנדרטיות כגון ‪C, C++,‬‬
‫‪ C#, Java, Python‬וכדומה‬
‫מהדורה שנייה – ‪ 25‬בפברואר ‪2009‬‬
‫נכתב על ידי ברק איטקין‬
‫עמוד ‪ | 0‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫מבוא‬
‫מדריך זה נכתב עקב מחסור בחומר בעברית בנוגע לכל הקשור בתכנות בשפת ‪.scheme‬‬
‫פרויקט התוכנה שלי היה משחק סנייק‪ ,‬אבל לא יכולתי לסיים אותו עם החומר שמצאתי בכיתה‬
‫או ברשת‪ .‬לצערי גיליתי שממשק משתמש הוא נושא שכמעט ואין עליו חומר‪ ,‬וחומר בעברית‬
‫בכלל אין‪ ,‬ובלי ממשק משתמש יש מעט מאוד משחקים שאפשר לבנות‪.‬‬
‫לכן‪ ,‬התחלתי לקרוא את התיעוד של ‪ ,scheme‬וחיפשתי מדריכים ברשת עד שמצאתי את‬
‫החומר שחיפשתי‪ .‬במדריך הזה יש את החומר שהצלחתי למצוא‪ ,‬מתורגם לעברית ומסוכם‬
‫ברמה שאמורה להספיק לסיום הפרויקט בתכנות פונקציונאלי‪.‬‬
‫הפרק הראשון הוא מבוא לשפה עצמה‪ ,‬שמיועד לאנשים שיש להם רקע בתכנות‪ .‬המבוא לא‬
‫מיועד להחליף למידה רגילה של השפה‪ ,‬אלא בא לסכם בקצרה את החומר הדרוש ולהקל על‬
‫המעבר ל‪ scheme‬למתכנתים בשפות אחרות‪.‬‬
‫המדריך מיועד לצרכים לימודיים‪ ,‬ולכן ניתן להפיץ אותו‪ ,‬אבל אך ורק בחינם וללא מטרות רווח‪.‬‬
‫אם אתם מעוניינים לתרגם את המדריך‪ ,‬הרגישו חופשי לעשות זאת‪ ,‬אבל הקפידו לשמור על‬
‫מתן קרדיט לברק איטקין‪.‬‬
‫בהצלחה בפרויקט =(‬
‫עמוד ‪ | 1‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫הקדמה‬
‫בשפת ‪ ,scheme‬כמעט כל דבר הוא פונקציה‪ .‬התחביר בשפה זו כדי לקרוא לפונקציה הוא‬
‫)… פרמטר_‪ 2‬פרמטר_‪ 1‬שם_הפונקציה(‬
‫ואם הפונקציה לא קולטת דבר‪ ,‬נקרא לה פשוט כך‪:‬‬
‫)שם_הפונקציה(‬
‫בגלל שכמעט כל דבר הוא פונקציה‪ ,‬נוצרת דרך כתיבה מאוד ייחודית‪ .‬לדוגמה‪ :‬חיבור )‪ (+‬היא פונקציה‪,‬‬
‫ולכן על מנת לחבר שני מספרים ‪ a‬ו‪ b-‬נכתוב באופן הבא‪:‬‬
‫)‪(+ a b‬‬
‫פעולת החיבור יכולה לקבל יותר משני מספרים באותו זמן‪ ,‬ולכן אפשר גם לרשום כך‪:‬‬
‫)‪(+ a b c 3 8 5‬‬
‫הנה טבלת סיכום של הפונקציות הסטנדרטיות‪:‬‬
‫הפונקציה בשפה רגילה‬
‫הפונקציה ב‪scheme‬‬
‫=< ‪ -‬אופרטור בוליאני קטן‪/‬שווה‬
‫=> ‪ -‬אופרטור בוליאני גדול‪/‬שווה‬
‫)‪(<= 2 5‬‬
‫יחזיר אמת – כי אכן מתקיים ‪2<=5‬‬
‫)‪(<= 2 5 6‬‬
‫> ‪ -‬אופרטור בוליאני קטן‬
‫< ‪ -‬אופרטור בוליאני גדול‬
‫יחזיר אמת – כי אכן מתקיים ‪2<=5<=6‬‬
‫)‪(<= 2 5 3‬‬
‫יחזיר שקר – כי לא מתקיים ‪5<=3‬‬
‫באותו אופן נשתמש גם הפונקציות הבוליאניות‬
‫האחרות המדברות על אי שוויונים‬
‫== ‪ -‬אופרטור בוליאני שווה )השוואת מספרים‬
‫בלבד(‪.‬‬
‫)‪(= 2 5‬‬
‫יחזיר שקר – כי ‪ 2‬לא שווה ‪5‬‬
‫)‪(= 5 5.0‬‬
‫יחזיר אמת – שימו לב שמתקבל שוויון גם עבור‬
‫מספרים מסוגים שונים‬
‫)פרמטר_‪ 2‬פרמטר_‪(equal? 1‬‬
‫‪ – Equal‬השוואת ערכים שאינם מספריים‬
‫לדוגמה‪:‬‬
‫)"‪(equal? "hello" "he‬‬
‫יחזיר ערך שקר‪ ,‬מכיוון שהמחרוזות לא שוות‪.‬‬
‫&& ‪ -‬אופרטור בוליאני וגם‬
‫)‪ and‬בשפת ‪(python‬‬
‫)‪(and ...‬‬
‫במקום ה ‪ ...‬ניתן לשים כמה ביטויים בוליאנים‬
‫שרוצים‬
‫))‪(and (< 5 2) (> 7 3) (= 9 3‬‬
‫יחזיר שקר‪ ,‬כי הביטוי הראשון הוא שקרי‬
‫|| ‪ -‬אופרטור בוליאני או‬
‫)… ‪(or‬‬
‫)‪ or‬בשפת ‪(python‬‬
‫! ‪ -‬אופרטור בוליאני לא )הופך אמת לשקר ולהפך(‬
‫)‪ not‬בשפת ‪(python‬‬
‫)ביטוי_בוליאני ‪(not‬‬
‫לדוגמה‪:‬‬
‫))‪(not (< 5 3‬‬
‫יחזיר אמת כי הביטוי הבוליאני מחזיר שקר‬
‫= ‪ -‬הצבה‬
‫לדוגמה – נציב את הערך ‪ 5‬במשתנה ‪:X‬‬
‫עמוד ‪ | 2‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫)‪(set! X 5‬‬
‫יש לציין שלא כך מציבים ערך במערך‪/‬רשימה‪.‬‬
‫בהצבה בתוך רשימה של משתנים נדון בהמשך‬
‫‪ - +‬חיבור‬
‫כל פעולות הללו יעשו על האיבר הראשון‪ .‬לדוגמה‪:‬‬
‫‪ - -‬חיסור‬
‫)‪(+ 5 3 6 7 8 2‬‬
‫* ‪ -‬כפל‬
‫מקביל להוראה ‪ 5+3+6+7+8+2‬בשפות אחרות‬
‫‪ - /‬חילוק )מדויק(‬
‫בדומה לכך‬
‫)‪(* 8 4 2‬‬
‫יחזיר ‪ 8*4*2‬כלומר ‪.64‬‬
‫שימו לב!‬
‫בשפת ‪ scheme‬פעולת החילוק הסטנדרטית‬
‫מחזירה חילוק מדוייק ולכן‬
‫)‪(/ 8 5 2‬‬
‫יחזיר ‪ 8/5/2‬כפי שנקבל במחשבון – כלומר ‪,0.8‬‬
‫למרות שזה חילוק של שלמים!‬
‫‪ - /‬חילוק )שלמים(‬
‫)מחלק מספר ‪(quotient‬‬
‫לדוגמה‪:‬‬
‫)‪(quotient 2 3‬‬
‫יחזיר ‪0‬‬
‫‪ - %‬שארית‬
‫)מחלק מספר ‪(remainder‬‬
‫לדוגמה‪:‬‬
‫)‪(remainder 5 2‬‬
‫יחזיר ‪1‬‬
‫כדי להוסיף הערה לקטע תוכנית‪ ,‬נשים בתחילת השורה נקודה פסיק ;‬
‫!‪; This is a comment‬‬
‫)"‪(write "comment test‬‬
‫עמוד ‪ | 3‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫פרק ‪ :0‬דברים שחייבים לדעת‬
‫משתנים‪:‬‬
‫כל המשתנים בשפת ‪ scheme‬הם ללא סוג‪ ,‬ולכן ניתן להציב ערך ממשי במשתנה שהכיל מספר שלם‪,‬‬
‫ניתן להציב מחרוזת במשתנה מסוג שבר‪ ,‬וכד'(‪.‬‬
‫בשפת ‪ scheme‬גם תמונה היא משתנה סטנדרטי‪ ,‬ולכן ע"י התפריט ‪ Insert‬שנמצא למעלה בחלון‪ ,‬ניתן‬
‫לבחור באפשרות ‪ Insert Image‬וקבל משהו כזה‪:‬‬
‫)‬
‫‪(define a‬‬
‫סימן )‪(symbol‬‬
‫ב‪ scheme‬קיים משתנה מיוחד מסוג סימן )‪ – (symbol‬משתנה זה הוא משתנה מיוחד שהוא לא מספר או‬
‫מחרוזת‪ .‬סימן מסומן ע"י גרש ' לפניו‪ .‬סימן הוא משתנה שימושי כאשר אנו רוצים להביע כמה מצבים של‬
‫משהו בלי ערך מספרי אלא ע"י הצהרה ברורה‪.‬‬
‫דוגמה‪ :‬נניח ויש לנו משתנה שמעיד על כיוון התזוזה של כדור‪ .‬הרבה יותר ברור יהיה אם המשתנה יכיל‬
‫את הערכים '‪ up 'down 'left 'right‬מאשר מספרים ‪.1 2 3 4‬‬
‫תווים )‪ (char‬ומחרוזות )‪(string‬‬
‫כדי להצהיר על מחרוזת‪ ,‬יש לתחום אותה בגרשיים כך‪ ."mystring" :‬יש להימנע מהסימן \ ואם רוצים‬
‫להשתמש בו‪ ,‬אז יש לכתוב אותו פעמיים ברצף – זו אחרי זו )בהדפסה הוא יופיע רק פעם אחת(‪ .‬כל‬
‫הסימנים המיוחדים משפות אחרות כגון ‪) \n‬ירידת שורה(‪) \" ,‬גרשיים(‪ ,‬ו‪) \t -‬טאב( קיימים גם כאן‪.‬‬
‫כדי להצהיר על תו יש לכתוב ‪ #\x‬כאשר את ‪ x‬מחליפים באות‪.‬בסימן הרצוי‪ .‬יש גם את המקרים המיוחדים‬
‫של תווי רווחים ‪#\newline #\tab #\space -‬‬
‫על מנת לבדוק את סוגי המשתנים‪ ,‬יש לנו פונקציות מובנות בשפה‪:‬‬
‫בדיקה האם משתנה הוא שלם‬
‫בדיקה האם משתנה הוא מספר‬
‫בדיקה האם משתנה הוא רשימה )ראה פרק ד'(‬
‫)‪(integer? X‬‬
‫)‪(number? X‬‬
‫)‪(list? X‬‬
‫בדיקה האם משתנה הוא וקטור )ראה פרק ד'(‬
‫)‪(vector? X‬‬
‫בדיקה האם משתנה הוא מחרוזת‬
‫)‪(string? X‬‬
‫בדיקה האם משתנה הוא תו‬
‫בדיקה האם משתנה הוא סימן‬
‫בדיקה האם משתנה הוא אי‪-‬זוגי‬
‫בדיקה האם משתנה הוא זוגי‬
‫)‪(char? X‬‬
‫)‪(symbol? X‬‬
‫)‪(odd? X‬‬
‫)‪(even? X‬‬
‫בדיקה האם משתנה הוא חיובי‬
‫)‪(positive? X‬‬
‫בדיקה האם משתנה הוא שלילי‬
‫)‪(negative? X‬‬
‫מדי פעם‪ ,‬יש צורך להמיר בין סוגי משתנים – גם לזה יש פונקציות‪:‬‬
‫המרת מספר למחרוזת‬
‫)‪(number->string X‬‬
‫המרת מחרוזת למספר‬
‫)‪(string->number X‬‬
‫המרת רשימת תווים למחרוזת‬
‫)‪(list->string X‬‬
‫המרת מחרוזת לרשימת תווים‬
‫)‪(string->list X‬‬
‫המרת סימן למחרוזת‬
‫עמוד ‪ | 4‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫)‪(symbol->string X‬‬
‫המרת מחרוזת לסימן‬
‫)‪(string->symbol X‬‬
‫על מנת לחבר ביחד מספר מחרוזות‪ ,‬יש להשתמש בפונקציה ‪:string-append‬‬
‫)… מחרוזת_‪ 2‬מחרוזת_‪(string-append 1‬‬
‫פלט‪:‬‬
‫פונקצית הדפסה‪:‬‬
‫)משתנה‪/‬ערך_להדפסה ‪(write‬‬
‫פונקצית הדפסה של מחרוזת )‪ (string‬מסוגלת להדפיס ירידת שורה‪:‬‬
‫)מחרוזת ‪(write-string‬‬
‫שימו לב שפונקציה זו מחזירה את האורך של המחרוזת‪ ,‬ולכן הכתיבה‬
‫)"‪(write-string "hello‬‬
‫תציג לנו ‪ ,hello‬ואם אין מי שיקלוט את הערך שהפונקציה מחזירה‪ ,‬נקבל ‪ .hello5‬על מנת לדאוג שלא‬
‫יוצג ‪ 5‬בפלט‪ ,‬יהיה עלינו להציב את הערך שהפונקציה מחזירה במשתנה כלשהו )ראה פרק ‪ (1‬באופן כזה‪:‬‬
‫))"‪(set! temp (write-string "hello‬‬
‫באופן זה‪ ,‬כל מה שיראה המשתמש הוא הפלט ‪ – hello‬בדיוק מה שרצינו‪.‬‬
‫המקרה בו נשתמש ב‪ write-string‬במקום ‪ write‬הוא כאשר המחרוזת שלנו עשויה להכיל סימנים‬
‫מיוחדים‪ ,‬ירידת שורה‪ ,‬טאבים וכד'‪.‬‬
‫קלט‪:‬‬
‫כדי לקלוט מהמשתמש ערך‪ ,‬פשוט נרשום )‪ .(read‬הערך הנקלט יזוהה באופן אוטומטי ע"י סביבת‬
‫העבודה ולכן אם נכניס מספר‪ ,‬אז הוא ייקלט ישר כמספר ולא כמחרוזת‪ .‬זה בניגוד לשפות כמו ‪ C#‬וכד'‬
‫שבהן אחרי שקיבלנו ערך מהמשתמש הוא נקלט כמחרוזת ואז היה צורך לעשות המרה של המשתנה‬
‫)‪.(parse‬‬
‫))‪(set! a (read‬‬
‫קריאה זו תציב במשתנה ‪ a‬את הערך שהמשתמש יכניס‪.‬‬
‫עמוד ‪ | 5‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫פרק ‪ :1‬הגדרת משתנים ופונקציות‬
‫הגדרות בסיסיות‬
‫בשפת ‪ ,scheme‬על מנת להגדיר משתנים‪/‬פונקציות נשתמש במילה השמורה ‪.define‬‬
‫הגדרת משתנה‬
‫)ערך_מוצב_ראשוני שם_המשתנה ‪(define‬‬
‫הגדרת פונקציה‪:‬‬
‫)פרמטרים_לקלט שם_הפונקציה( ‪(define‬‬
‫גוף הפונקציה‬
‫שימו לב שהחזרת ערך אינה חובה ולכן נכתבה בסוגריים מרובעים ; ]ערך מוחזר[‬
‫)‬
‫פונקציות לא חייבות להחזיר ערך‪ ,‬ולא חובה להצהיר על סוג הערך המוחזר‪ .‬על מנת להחזיר משתנה‪,‬‬
‫נכתוב את שמו‪/‬ערכו לא בתוך סוגריים‪.‬‬
‫לדוגמה‪:‬‬
‫)‪(define (hello1 num‬‬
‫)‪(+ num 1‬‬
‫)‬
‫כפי שניתן לראות‪ ,‬הפונקציה מחזירה את המשתנה ‪ num‬מוגדל באחד‪.‬‬
‫הערה חשובה‪ :‬אם פונקציה מנסה להחזיר מספר ערכים במהלך ביצועה )יש כמה ערכים שלא הוצבו‬
‫באף פונקציה‪/‬משתנה(‪ ,‬אזי הפונקציה תחזיר רק את הערך האחרון‪ .‬לכן‪ ,‬עבור הפונקציה‬
‫)‪(define (hello1 num‬‬
‫)‪(+ num 1‬‬
‫)‪(+ num 2‬‬
‫)‬
‫יוחזר אך ורק ‪ num‬מועלה ב‪.2-‬‬
‫כדי להציב ערך חדש בתוך משתנה יש להשתמש בפקודה !‪:set‬‬
‫)הערך_החדש המשתנה !‪(set‬‬
‫פונקציה היא פרמטר‬
‫בשפת ‪ ,scheme‬גם פונקציה היא סוג של פרמטר‪ ,‬ולכן ניתן להעביר אותה כקלט לפונקציה אחרת‪ .‬כעת‪,‬‬
‫נדגים פונקציה שקוראת לפונקציה אחרת ‪ 5‬פעמים‪:‬‬
‫)‪(define (do-5 myfunc‬‬
‫)‪(myfunc‬‬
‫)‪(myfunc‬‬
‫)‪(myfunc‬‬
‫)‪(myfunc‬‬
‫)‪(myfunc‬‬
‫)‬
‫עמוד ‪ | 6‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫הפונקציה ‪ do-5‬קולטת כפרמטר פונקציה ומהצעת אותה ‪ 5‬פעמים )בהנחה שהיא אינה קולטת אף‬
‫משתנה(‪ .‬אין חובה לקלוט את הפונקציה עם המזהה ‪ – myfunc‬יכולתי לקרוא לה בכל שם שאני רוצה‪,‬‬
‫שהוא לא שם של פונקציה שקיימת כרגע‪.‬‬
‫הדוגמה הזאת לא נראית שימושית כרגע‪ ,‬אבל בפרק ‪ – 6‬ממשק משתמש‪ ,‬אנו נשתמש בזה הרבה מאוד‪.‬‬
‫משתנים מקומיים – הפונקציה *‪let‬‬
‫במקרים רבים‪ ,‬נרצה להגדיר משתנים מקומיים – משתנים שיהיו זמינים רק לפונקציה מסוימת‪ ,‬ואף‬
‫פונקציה אחרת לא תוכל לגשת אליהם או לעדכן אותם‪ ,‬והם יוגדרו מחדש כל פעם בתחילת הפונקציה‪.‬‬
‫בשביל לעשות זאת‪ ,‬נשתמש בפונקציה *‪:let‬‬
‫)ערך_התחלתי שם_המשתנה(( *‪(let‬‬
‫)ערך_התחלתי שם_המשתנה(‬
‫…‬
‫)‬
‫פקודות_לביצוע‬
‫)‬
‫הפונקציה *‪ let‬תאפשר גישה למשתנים אך ורק לפקודות שנמצאות בתוכה‪ ,‬ולכן ברגע שיסתיימו כל‬
‫הפקודות לביצוע בתוך *‪ ,let‬פונקציות בהמשך התוכנית לא יוכלו לגשת למשתנים שהוצהרו רק בתוך‬
‫גוף ה‪.let*-‬‬
‫בד"כ נרצה שמשתנים מקומיים שנגדיר יהיו זמינים לכל הפונקציה ולא רק לחלקה‪ ,‬ולכן נהוג להצהיר על‬
‫פונקציות שצריכות משתנים מקומיים באופן הבא‪:‬‬
‫)‪(define (my-function number‬‬
‫)‪(let* ( (sign 0‬‬
‫)‬
‫)‪(cond [(> number 0‬‬
‫)‪(set! sign 'positive‬‬
‫]‬
‫‪[else‬‬
‫)‪(set! sign 'negative‬‬
‫]‬
‫)‬
‫)‪(write-string "The Number Is")(write sign‬‬
‫‪sign‬‬
‫)‬
‫)‬
‫לדוגמה‪ ,‬הפונקציה הזו תדפיס למשתמש האם מספר הוא חיובי או שלילי‪ ,‬ואז תחזיר בהתאמה‬
‫‪ 'positive‬או ‪ .'negative‬שימו לב שמכיוון שרק הערך האחרון בפונקציה הוא זה שמוחזר‪ ,‬היא לא‬
‫תחזיר את אורך המחרוזת כפי שמתקבל מהפונקציה ‪ ,write-string‬ולכן אין צורך לטפל בערך שהיא‬
‫מחזירה‪.‬‬
‫עמוד ‪ | 7‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫פרק ‪ :2‬תנאים‬
‫הקדמה‪ :‬בשפת ‪ scheme‬אין לולאות סטנדרטיות כגון ‪ for ,while‬וכד'‬
‫על מנת ליצור תהליך שחוזר על עצמו נשתמש ברקורסיה – ראה פרק ג'‪.‬‬
‫תנאי פשוט – ‪if‬‬
‫"ביטוי_בוליאני – "אם‬
‫‪(if‬‬
‫"משפט שמתבצע אם מוחזר ערך אמת ‪" -‬אז‬
‫"משפט שמתבצע אם מוחזר ערך שקר ‪" -‬אחרת‬
‫)‬
‫לדוגמה‪ ,‬נניח שיש לנו משתנה ‪ num‬שמכיל ערך כלשהו‪ .‬נרצה להציב בו ‪ 5‬אם הוא חיובי‪ ,‬ולהציב בו ‪ 6‬אם‬
‫הוא שלילי או אפס‪:‬‬
‫)‪(> num 0‬‬
‫‪(if‬‬
‫)‪(set! num 5‬‬
‫)‪(set! num 6‬‬
‫)‬
‫אבל מה יקרה אם נרצה שאם התנאי יקרה‪ ,‬יתבצע יותר מדבר אחד? הפיתרון הוא פשוט – נשתמש‬
‫בפקודה ‪ begin‬אשר מאחדת מספר פקודות לפקודה אחת‪.‬‬
‫)‪(> num 0‬‬
‫‪(if‬‬
‫‪(begin‬‬
‫)‪(set! num 5‬‬
‫)"‪(write "positive‬‬
‫)‬
‫‪(begin‬‬
‫)‪(set! num 6‬‬
‫)"‪(write "negative‬‬
‫)‬
‫)‬
‫תנאי מורכב – ‪cond‬‬
‫]פעולות_לביצוע )ביטוי_בוליאני_‪(cond [(1‬‬
‫]פעולות_לביצוע )ביטוי_בוליאני_‪[(2‬‬
‫…‬
‫]פעולות_לביצוע ‪[else‬‬
‫)‬
‫משפט ה‪) else‬מה שמתקיים אם אף אחד מהאחרים לא מתקיים( הוא אופציונאלי ואינו חובה‪.‬‬
‫ניתן להכניס יותר מפעולה אחת בתוך הסוגריים במקום בו רשום פעולות_לביצוע‪.‬‬
‫דוגמה‪:‬‬
‫)‪(cond [ (> x 0‬‬
‫])‪(set! sign 1‬‬
‫))‪(set! x (* x 2‬‬
‫)‪[(< x 0‬‬
‫])‪(set! sign -1‬‬
‫))‪(set! x (* x 3‬‬
‫‪[else‬‬
‫])‪(set! sign 0‬‬
‫)‬
‫עמוד ‪ | 8‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫קטע קוד זה יכפיל את ‪ x‬ב‪ 2-‬אם הוא חיובי‪ ,‬ב‪ 3-‬אם הוא שלילי וישאיר אותו אפס אם הוא אפס‪ .‬בנוסף‪,‬‬
‫במשתנה ‪ sign‬יוצב אחד‪/‬מינוס אחד‪/‬אפס בהתאמה לסימנו של ‪.x‬‬
‫שימו לב‪:‬‬
‫‪.1‬‬
‫בניגוד לפונקציה ‪ ,if‬פונקצית ‪ cond‬אינה דורשת שימוש ב‪ begin-‬גם אם יש בתוכה קריאה‬
‫למספר פונקציות‪.‬‬
‫‪.2‬‬
‫ברגע שיתבצע אחד מתוך המקרים של ‪ cond‬לא תמשיך הבדיקה עבור שאר המקרים‪.‬‬
‫‪.3‬‬
‫‪ else‬מתבצע אך ורק אם אף אחד מהמקרים לפניו אינו מתבצע‪.‬‬
‫‪.4‬‬
‫פונקציות תנאי מקבלות כל ערך שאינו ‪) false‬או כפי שלעיתים הוא נכתב ‪ (#f -‬כביטוי אמת‪,‬‬
‫ולכן אם במקום הביטוי הבוליאני יהיה רשום מספר‪ ,‬סימן או כל דבר שאינו ‪ false‬אז זה יפורש‬
‫כאילו נכתב ‪.true‬‬
‫עמוד ‪ | 9‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫פרק ‪ :3‬רשימות )‪ (list‬ומחרוזות )‪(string‬‬
‫בדומה למערך בשפות אחרות‪ ,‬ב ‪ scheme‬קיים משתנה הרשימה אשר יכול להכיל מספר משתנים‪.‬‬
‫שימו לב‪ :‬משתנה זה אינו אובייקט‪ ,‬ולכן אם נעביר אותו לפונקציה יועבר הערך ממש שלו‪ ,‬ולא הצבעה על‬
‫תא זיכרון‪ .‬כתוצאה מכך‪ ,‬אין אפשרות לעדכן ישירות רשימה‪ ,‬ויש צורך להציב בה את הערך ע"י פקודת‬
‫ההצבה !‪.set‬‬
‫רשימה של איברים קיימים‪:‬‬
‫על מנת ליצור רשימה של איברים קיימים‪ ,‬נשתמש בפקודה ‪:list‬‬
‫) … איבר_‪ 2‬איבר_‪(list 1‬‬
‫לדוגמה‪ :‬נניח וברשותי משתנים ‪ .a=3, b=4, c=5‬אזי מהקריאה‬
‫))‪(list 2 a b 5 (add1 c‬‬
‫נקבל את הרשימה )‪.(2 3 4 5 6‬‬
‫ניתן להצהיר ישירות על רשימה גם באופן הבא‪:‬‬
‫)… איבר_‪ 3‬איבר_‪ 2‬איבר_‪'(1‬‬
‫שימו לב – הצהרה כזו היא מסוכנת‪.‬‬
‫אם נציב בה ערכים ממש אז לא ניתקל בשום בעיה )לדוגמה ')‪ ,((1 2 3‬אבל אם נציב בה משתנים או‬
‫פונקציות ניתקל בבעיה‪ .‬לדוגמה‪ ,‬נניח ויש לנו משתנה ‪ x‬עם הערך ‪.2‬‬
‫)‪(list x 3 4‬‬
‫יחזיר כצפוי )‪ ,(2 3 4‬אבל‬
‫)‪'(x 3 4‬‬
‫יחזיר את הרשימה )‪ x – (x 3 4‬מפוענח פה כסימן ולא בתור הערך שלו‪ .‬ההצהרה הנ"ל שקולה להצהרה‬
‫)‪(list 'x 3 4‬‬
‫גישה לאיברי רשימה‪ ,‬אורכה של רשימה וכד'‬
‫)אינדקס רשימה ‪(list-ref‬‬
‫כאשר האיבר הראשון הוא אינדקס ‪ ,0‬השני הוא אינדקס ‪ ,1‬וכד'‪.‬‬
‫יש גם פעולות נוספות שנוכל לבצע על רשימה‪:‬‬
‫מחזיר את האורך של רשימה‬
‫)רשימה ‪(length‬‬
‫מחזיר את הרשימה עם סדר איברים הפוך‬
‫)רשימה ‪(reverse‬‬
‫מחזירה את האיבר הראשון ברשימה‬
‫)רשימה ‪(first‬‬
‫מחזירה את האיבר השני ברשימה‬
‫)רשימה ‪(second‬‬
‫מחזירה את האיבר השלישי ברשימה‬
‫)רשימה ‪(third‬‬
‫מחזירה את כל הרשימה בלי האיבר הראשון‬
‫מחזיר רשימה המורכבת מאיברי הרשימה הראשונה‬
‫)רשימה ‪(rest‬‬
‫)רשימה_‪ 2‬רשימה_‪(append 1‬‬
‫ומיד אחריהם איברי הרשימה השנייה‬
‫אין פונקציה להציב איבר אחד במקום איבר אחר בתוך רשימה – דרך לעשות זאת מוסברת בחלק ד'‪.‬‬
‫מחרוזות‬
‫)אינדקס מחרוזת ‪(string-ref‬‬
‫כאשר התו הראשון הוא אינדקס ‪ ,0‬השני הוא אינדקס ‪ ,1‬וכו'‪.‬‬
‫בדומה לרשימה‪ ,‬גם על מחרוזת )שהיא בעצם רשימת תווים( ניתן להפעיל פעולות דומות לאלו שיש לנו‬
‫עבור רשימות‪:‬‬
‫עמוד ‪ | 10‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫מחזירה את האורך של מחרוזת‬
‫)מחרוזת ‪(string-length‬‬
‫)‪ i‬מחרוזת ‪(substring‬‬
‫מחזירה מחרוזת המכילה את כל התווים החל מהמקום ה‪'i'-‬‬
‫)התו הראשון הוא המקום ‪ ,0‬השני הוא המקום ‪ 1‬וכך הלאה(‬
‫)‪ i j‬מחרוזת ‪(substring‬‬
‫מחזירה מחרוזת המכילה את כל התווים החל מהמקום ה‪'i'-‬‬
‫ועד למקום ה‪) 'j'-‬לא כולל המקום ה‪('j'-‬‬
‫מחזירה מחרוזת המורכבת מתווי ‪ s1‬ומייד אחריהם תווי ‪,s2‬‬
‫)… ‪(string-append s1 s2 s3‬‬
‫תווי ‪ ,s3‬וכך הלאה‬
‫רשימת זוגות – הפונקציה ‪cons‬‬
‫)פרמטר_‪ 2‬פרמטר_‪(cons 1‬‬
‫הפונקציה ‪ cons‬מחזירה זוג סדור‪/‬רשימה שמורכבת מהפרמטר הראשון עם הפרמטר השני‪:‬‬
‫)‪(cons a b‬‬
‫‪ a‬הוא רשימה‬
‫לדוגמה ‪(list 1 'up‬‬
‫‪ b‬הוא רשימה‬
‫‪ b‬הוא אינו רשימה‬
‫לדוגמה )"‪(list 'down 3 "hello‬‬
‫לדוגמה ‪3‬‬
‫נקבל את הרשימה המורכבת מ‪a-‬‬
‫נקבל את הזוג הסדור המורכב מ‪-‬‬
‫כרשימה בתור האיבר הראשון‪ ,‬ואיברי‬
‫‪ a‬כרשימה בתור האיבר הראשון‪,‬‬
‫‪ b‬אח"כ‪.‬‬
‫ו‪ -b‬אח"כ‪.‬‬
‫)‪2‬‬
‫)"‪((1 'up 2) 'down 3 "hello‬‬
‫נקבל את הרשימה המורכבת מ‪ a-‬בתור‬
‫‪ a‬הוא אינו רשימה‬
‫האיבר הראשון‪ ,‬ואיברי ‪ b‬אח"כ‪.‬‬
‫לדוגמה ‪2‬‬
‫)"‪(2 'down 3 "hello‬‬
‫)‪((1 'up 2) . 3‬‬
‫נקבל את הזוג הסדור המורכבת‬
‫מ‪ a-‬בתור האיבר הראשון ו‪b-‬‬
‫אח"כ‪.‬‬
‫)‪(2 . 3‬‬
‫בעתם ניתן לראות שמוחזרת רשימה המורכבת מ‪ a-‬ומייד אחריו ‪ ,b‬כאשר אם ‪ b‬רשימה אז נקבל רשימה‬
‫עם איברי ‪ ,b‬ואחרת נקבל זוג סדור‪.‬‬
‫ננתח רגע את הפקודה הבאה‪:‬‬
‫)))) )(' ‪(cons 1 (cons 2 (cons 3 (cons 4‬‬
‫)‪(2 3 4‬‬
‫)‪(3 4‬‬
‫)‪(4‬‬
‫))) )(' ‪(cons 2 (cons 3 (cons 4‬‬
‫תרשים ‪ – 3.1‬ניתוח של קינון קריאות של ‪ cons‬ליצירת רשימה‬
‫קיבלנו רשימה עם איבר אחד מהקריה הפנימית ביותר )איבר ‪ +‬איברי רשימה ריקה( ובאופן זה המשכנו‪.‬‬
‫קיבלנו דרך לבנות רשימה מאיברים בודדים!‬
‫בחלק ד' – רקורסיה נדון בניצול של שיטה זו כדי להציב ערכים ברשימות קיימות!‬
‫עמוד ‪ | 11‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫פרק ‪ :4‬רקורסיה )‪ (recursion‬ככלי לפישוט בעיות ולולאות רקורסיביות‬
‫לולאות רקורסיביות‬
‫רקורסיה היא נושא שהוא פחות מסובך ממה שחלק מהאנשים מתארים אותו‪ ,‬כי הרעיון הוא די פשוט –‬
‫לקרוא לפעולה מתוך עצמה‪.‬‬
‫דוגמה – נרצה להדפיס למסך את כל המספרים ממספר התחלתי כלשהו ‪ i‬ועד למספר סופי ‪.end‬‬
‫אם היו לנו לולאות – יש להניח שהיינו מבצעים זאת באופן דומה לאופן הבא כך )קטע הקוד הבא הוא לא‬
‫ב‪:(scheme-‬‬
‫‪Python‬‬
‫‪C, C++, C#, Java‬‬
‫‪while i < end :‬‬
‫{ ) ‪while (i < end‬‬
‫‪print i‬‬
‫;)‪Print(i‬‬
‫‪i+=1‬‬
‫;‪i++‬‬
‫}‬
‫רגע‪ ,‬מה זו לולאת ‪ – ?while‬זו לולאה שמבצעת את עצמה עוד פעם ועוד פעם ועוד פעם עד שתנאי‬
‫מסוים קורה‪ .‬אז פשוט נבנה את התהליך שמתבצע עוד פעם כפונקציה עם תנאי על כל הגוף שלה‪,‬‬
‫שבסופה תקרא לעצמה עוד פעם!‬
‫"קלט" הלולאה – קליטת הערכים שבהם‬
‫משתמשת הפונקציה‪ .‬זו בעצם הכרזה על‬
‫הפונקציה – קליטה של ערך התחלה )ערך נוכחי(‬
‫)‪(define (my-print-func i end‬‬
‫וערך סיום‬
‫תנאי הלולאה ‪ -‬נבדוק האם הערך הנוכחי לא גדול‬
‫מערך הסיום‬
‫ביצוע גוף הלולאה‪ ,‬כולל קידום המשתנה שקשור‬
‫לתנאי הלולאה‬
‫קריאה לסבב להפעלה הבאה של הלולאה‬
‫)‪(cond [ (<= i end‬‬
‫)‪(write i‬‬
‫)"‪(write-string "\n‬‬
‫))‪(set! i (+ 1 i‬‬
‫)‪(my-print-func i end‬‬
‫]‬
‫סגירת כל הסוגריים‬
‫)‬
‫)‬
‫וכצפוי‪ ,‬הפלט עבור הקריאה )‪ (my-print-func 20 40‬וכצפוי הפלט יהיה כל המספרים מ‪ 20-‬ועד ‪.40‬‬
‫המסקנה היא פשוטה ואפקטיבית – כל לולאת ‪ while‬ניתן להגדיר כפונקציה רקורסיבית באופן הנ"ל‪.‬‬
‫שימו לב – זו המרה ישירה של לולאה לרקורסיה‪ ,‬אבל זו לא הדרך היחידה להשתמש ברקורסיה‪.‬‬
‫רקורסיה ככלי לפישוט בעיות‬
‫בניגוד למה שראינו למעלה‪ ,‬המטרה של רקורסיה היא לא תמיד לולאות‪ .‬רקורסיה היא גם תהליך‬
‫המשמש לפיתרון בעיות מסובכות עם הרבה מרכיבים‪ ,‬כאשר אנחנו יודעים לפתור את הבעיה עבור‬
‫מרכיבים מסוימים‪ ,‬ואת שאר המקרים אנחנו מסוגלים לפתור באמצעות מקרים שקדמו להם‪.‬‬
‫סביר להניח שמה שהרגע קראתם לא היה מובן בקריאה ראשונה‪ ,‬ולכן ניתן דוגמה שימושית‪:‬‬
‫הטור של פיבונאצ'י‬
‫הטור של פיבונצ'י זו סדרה שבה האיברים הראשונים הם אפס ואחד‪ ,‬ואחר כך כל איבר הוא סכום של שני‬
‫האיברים הקודמים‪ .‬זו דוגמה קלאסית למקרה שבו אנחנו יודעים פיתרון עבור מקרים מסוימים ואת‬
‫המקרים האחרים ניתן להביע באמצעות מקרים שקדמו להם‪ .‬נראה כעת השוואה בין שני פתרונות של‬
‫עמוד ‪ | 12‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫מציאת האיבר במקום ה‪ n-‬בסדרה‪ .‬סביר להניח שאם הייתם פותרים בשפה שאליה אתם רגילים‪ ,‬זה היה‬
‫נראה בערך ככה‪:‬‬
‫‪Python‬‬
‫‪C, C++, C#, Java‬‬
‫‪def fibo(n):‬‬
‫{ )‪int fibo(int n‬‬
‫‪fiboA = 1‬‬
‫;‪int fiboA = 1, fiboB = 0‬‬
‫‪fiboB = 0‬‬
‫;‪int i = 0, temp = 0‬‬
‫‪temp = 0‬‬
‫)‪if (n == 1‬‬
‫;‪return 0‬‬
‫‪if n == 1:‬‬
‫)‪else if (n == 2‬‬
‫‪return 0‬‬
‫;‪return 1‬‬
‫‪elif n == 2:‬‬
‫‪return 1‬‬
‫{ ) ‪for ( i = 3 ; i <= n ; i ++‬‬
‫;‪temp = fiboA‬‬
‫‪for i in range(3,n+1): #3...n‬‬
‫‪temp = fiboA‬‬
‫;‪fiboA = fiboB + fiboA‬‬
‫‪fiboA = fiboB + fiboA‬‬
‫;‪fiboB = temp‬‬
‫}‬
‫‪fiboB = temp‬‬
‫;‪return fiboA‬‬
‫‪return fiboA‬‬
‫}‬
‫אבל באמצעות רקורסיה – זה הרבה יותר קל והרבה יותר אינטואיטיבי‪:‬‬
‫)‪(define (fibbonaci n‬‬
‫‪(cond‬‬
‫)‪((= n 1) 0‬‬
‫)‪((= n 2) 1‬‬
‫האיבר שנמצא מקום אחד אחורה;‬
‫האיבר שנמצא שני מקומות אחורה;‬
‫))‪(else (+ (fibbonaci (- n 1‬‬
‫))‪(fibbonaci (- n 2‬‬
‫)‬
‫)‬
‫)‬
‫)‬
‫זה מקרה שבו באמת‪ ,‬רקורסיה היא אחת הדרכים הכי פשוטות שיש לפתור בעיה – אנחנו יודעים‬
‫להתמודד עם מצב התחלתי‪ ,‬ואנו יודעים איך מביעים כל מצב באמצעות המצבים שקדמו לו‪ .‬את הפתרון‬
‫הרקורסיבי הזה ניתן ליישם ברוב השפות‪ ,‬כולל השפות בהן אנו משתמשים בדרך כלל‪.‬‬
‫מכיוון שכל קריאה קוראת לפונקציה עם אינדקס ‪ n‬יותר נמוך‪ ,‬זה יגיע מתישהו לאחד מהערכים‬
‫ההתחלתיים‪ ,‬שאותם הפונקציה שלנו יודעת להחזיר‪.‬‬
‫פתרון באמצעות רקורסיה ככלי לפישוט בעיות לעומת לולאה רקורסיבית‬
‫בדף הבא מוצגות שתי דרכים למצוא סכום של כל המספרים מ‪ start-‬ועד ‪ – end‬פעם אחת ברקורסיה‬
‫ככלי לפישוט בעיות‪ ,‬ופעם אחת באמצעות לולאה רקורסיבית‪.‬‬
‫עמוד ‪ | 13‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫רקורסיה ככלי לפישוט בעיות‬
‫לולאה רקורסיבית‬
‫)‪(define (iterate-sum start end‬‬
‫)‪(define (recursive-sum start end‬‬
‫)‪(loop-sum-do-it start end 0‬‬
‫)‪(cond [ (< start end‬‬
‫)‬
‫))‪(+ start (recursive-sum (add1 start) end‬‬
‫]‬
‫)‪(define (iterate-sum-do-it start end sum‬‬
‫‪[else‬‬
‫)‪(cond [ (<= start end‬‬
‫‪end‬‬
‫))‪(set! sum (+ sum start‬‬
‫]‬
‫))‪(set! start (add1 start‬‬
‫)‬
‫)‪(loop-sum-do-it start end sum‬‬
‫)‬
‫]‬
‫‪[else‬‬
‫‪sum‬‬
‫]‬
‫)‬
‫)‬
‫פה יש לנו המרה של לולאת ‪ while‬רגילה – כל עוד ‪ start‬לא גדול מ‪ end‬נוסיף אותו‬
‫פה יש לנו פיתרון שמנצל רקורסיה ככלי לפישוט בעיות‪ .‬אנחנו יודעים שהסכום של כל‬
‫לסכום‪ ,‬ונעלה את ‪ start‬באחד‪ .‬פתרון זה מנצל רקורסיה רק ככלי לביצוע לולאה‪ ,‬ולא‬
‫המספרים מ‪ start-‬עד ‪ end‬כאשר ‪ start‬שווה ל‪ end-‬זה פשוט ‪ .end‬אנחנו גם יודעים‬
‫ככלי לפישוט בעיות‪ .‬המשתנה ‪ sum‬מוחזר בסוף ביצוע הלולאה ע"י כך שהוא נמצא‬
‫שהסכום של כל המספרים מ‪ start-‬ועד ‪ end‬הוא פשוט כמו ‪ start‬ועוד הסכום‬
‫בחלק שמתבצע רק בסוף הלולאה ‪.else‬‬
‫ממקום אחד קדימה )‪ (+ 1 start‬ועד ‪ .end‬בצורה זו כל פעם נקרא לפונקציה שלנו‬
‫המשתנה ‪ sum‬שומר את הסכום שנצבר עד כה‪ ,‬ולכן כדי שהמשתמש לא יצטרך‬
‫עם ‪ start‬יותר גבוה‪ ,‬עד שבסוף נקרא לה כאשר ‪ start‬שווה ל‪ end-‬וזה כבר משהו‬
‫להכניס אפס בהתחלה‪ ,‬יש לנו פונקציה אחת שקוראת לפונקציה שהיא הלולאה ממש‪.‬‬
‫שהפונקציה יודעת לטפל בו‪.‬‬
‫הסבר‬
‫שני הפתרונות יחזירו בדיוק את אותה התוצאה‪ ,‬אבל הם עובדים בדרכים שונות‪ .‬אחד מהם הוא פשוט המרה של לולאת ‪ while‬לרקורסיה‪ ,‬והשני מנצל את העובדה שיודעים‬
‫לסכום מצב מסוים‪ ,‬ואת המצבים האחרים אנו יודעים לפשט למצב המסוים הזה‪.‬‬
‫עמוד ‪ | 14‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫פרק ‪ :5‬בניית משחק מטריציונלי )משחק לוח(‬
‫בשביל רוב משחקי הלוח )דמקה‪ ,‬שחמט‪ (...,‬והמשחקים שמבוססים על טבלה של מקומות )סנייק‪,‬‬
‫טטריס‪ ,‬באבלס‪ , ,‬פקמן‪ ,‬שולה המוקשים‪ ,‬רוורסי‪ (..,‬יש צורך בטבלה אשר מייצגת מה נמצא איפה‪ .‬זה לא‬
‫נשמע יותר מדי נורא‪ ,‬למעט העובדה שאין דבר כזה טבלה ב‪ ,scheme-‬ובטח שלא טבלה עם פונקציות‬
‫לעדכון וגישה לערכים שבה‪.‬‬
‫אז מה עושים?בונים טבלה בעצמנו‪.‬‬
‫טבלה‬
‫הרעיון‪ :‬אנחנו מסוגלים לבנות שורה ע"י רשימה ‪ list‬אשר נמצא כבר בשפה‪ .‬אם נבנה רשימה של‬
‫רשימות‪ ,‬נקבל טבלה!‬
‫כל אחת מהרשימות הללו היא איבר‬
‫ברשימה אחת ראשית שמייצגת את‬
‫הטבלה!‬
‫)‬
‫)‬
‫)‬
‫)‬
‫)‬
‫‪...‬‬
‫‪...‬‬
‫‪...‬‬
‫‪...‬‬
‫‪...‬‬
‫‪a5‬‬
‫‪b5‬‬
‫‪c5‬‬
‫‪d5‬‬
‫‪e5‬‬
‫‪a4‬‬
‫‪b4‬‬
‫‪c4‬‬
‫‪d4‬‬
‫‪e4‬‬
‫‪a3‬‬
‫‪b3‬‬
‫‪c3‬‬
‫‪d3‬‬
‫‪e3‬‬
‫‪a2‬‬
‫‪b2‬‬
‫‪c2‬‬
‫‪d2‬‬
‫‪e2‬‬
‫‪a1‬‬
‫‪b1‬‬
‫‪c1‬‬
‫‪d1‬‬
‫‪e1‬‬
‫(‬
‫(‬
‫(‬
‫(‬
‫(‬
‫תרשים ‪ :5.1‬ניתוח המבנה של טבלה‬
‫הבעיות העומדות בפנינו הן‪:‬‬
‫‪.1‬‬
‫איך יוצרים רשימה באורך ‪?n‬‬
‫‪.2‬‬
‫איך מעדכנים איבר במקום ה‪ n-‬ברשימה?‬
‫‪.3‬‬
‫איך יוצרים טבלה ברוחב ‪ x‬ובגובה ‪?y‬‬
‫‪.4‬‬
‫איך ניגשים בטבלה לאיבר בשורה ה‪ i-‬ובעמודה ה‪?j-‬‬
‫‪.5‬‬
‫איך מעדכנים איבר בטבלה בשורה ה‪ i-‬ובעמודה ה‪?j-‬‬
‫את הבעיה השנייה אני הולך לפתור‪ ,‬משום שהבנה שלה היא כל מה שצריך כדי לפתור את שאר הבעיות‪.‬‬
‫הגדרת פעולה לעדכון רשימה‪:‬‬
‫נסתכל על הפעולות העומדות לרשותנו‪ ,‬אשר עשויות להיות רלוונטיות‪:‬‬
‫‪.1‬‬
‫קבלת האורך של רשימה‬
‫‪.2‬‬
‫גישה לאיבר הראשון )גם לשני‪ ,‬ולשלישי( ברשימה‬
‫‪.3‬‬
‫גישה לאיבר במקום ה‪ n-‬ברשימה‬
‫‪.4‬‬
‫בדיקה – האם רשימה היא ריקה?‬
‫‪.5‬‬
‫גישה לכל האיברים אחרי האיבר הראשון‬
‫‪.6‬‬
‫הוספת איברים לסדרה )באמצעות ‪ cons‬ובאמצעות ‪(append‬‬
‫ננתח את הבעיה‪:‬‬
‫יש לנו רשימה‪ ,‬באורך כלשהו‪ .‬אנחנו רוצים לגשת לאיבר במקום ה‪ n-‬ולהחליף אותו באיבר אחר‪ .‬לצורך‬
‫כך‪ ,‬נרצה לקבל את כל איברי הסדרה לפני המקום ה‪ .n-‬אח"כ נציב את הערך המעודכן אחרי האיברים‬
‫שקיבלנו‪ ,‬ומשם נציב שוב את שאר הסדרה‪.‬‬
‫יש לנו אפשרות לגשת ל"ראש" הרשימה )האיבר במקום הראשון(‪ ,‬ולזנב הרשימה )רשימת כל האיברים‬
‫אחרי המקום הראשון(‪ .‬אם נקבל רשימה שבה רוצים לשנות את איבר הראשון‪ ,‬זה קל‪ .‬נחבר באמצעות‬
‫הפקודה ‪ cons‬את הערך החדש‪ ,‬עם הזנב של הרשימה‪ .‬אם נקבל רשימה שבה רוצים לשנות את המקום‬
‫השני‪ ,‬נחבר לראש הרשימה באמצעות ‪ cons‬את הזנב לאחר שבו עודכן האיבר במקום הראשון‪ .‬באופן זה‬
‫נמשיך כפי שמתואר בשרטוט שבדף הבא‪:‬‬
‫עמוד ‪ | 15‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫רוצים לעדכן את‬
‫האיבר ה‪n-‬‬
‫רוצים לעדכן את‬
‫האיבר ה‪n-1-‬‬
‫רוצים לעדכן את‬
‫האיבר ה‪n-2-‬‬
‫רשימה‬
‫זנב‬
‫הרשימה‬
‫זנב‬
‫הרשימה‬
‫רוצים לעדכן את‬
‫האיבר ה‪0-‬‬
‫זנב הרשימה‬
‫והאיבר שרצינו ראשון‬
‫זנב‬
‫הרשימה‬
‫ערך‬
‫חדש‬
‫ראש‬
‫הרשימה‬
‫ראש‬
‫הרשימה‬
‫ראש‬
‫הרשימה‬
‫תרשים ‪ – 5.1‬ניתוח לוגי של רקורסיה לעדכון רשימה‬
‫פה יש לנו פונקציה רקורסיבית‪ ,‬עם ‪ cons‬שמקבל כאחד מהפרמטרים שלו קריאה רקורסיבית – זו הדוגמה‬
‫הכי שימושית בפרויקט ליישום של רקורסיה ככלי לפתרון בעיות‪.‬‬
‫)‪(define (update-list mylist pos val‬‬
‫)‪(cond [(> pos 0‬‬
‫)‪(cons (first mylist‬‬
‫)‪(update-list (rest mylist) (sub1 pos) val‬‬
‫)‬
‫]‬
‫)‪[(= pos 0‬‬
‫))‪(cons val (rest mylist‬‬
‫]‬
‫)‬
‫)‬
‫ניתוח הפונקציה‪:‬‬
‫תזכורת‪ cons :‬שמקבל איבר בודד ואז רשימה יחזיר רשימה המורכבת מהאיבר הבודד ואחריו איברי‬
‫הרשימה שנקלטה‪.‬‬
‫אם מדובר על עדכון המקום שאינו הראשון‪ ,‬נחבר את המקום הראשון לרשימה עם עדכון של הזנב‪.‬‬
‫אם מדובר על עדכון המקום הראשון – זה המקרה הטריוויאלי שבו דנו ובו פשוט נחבר את הערך החדש‬
‫עם הזנב‪ .‬מכיוון שכל הזמן המיקום של האיבר לעדכון יורד באחד )כי מורידים איבר מההתחלה( אז ברגע‬
‫מסוים האיבר הרצוי יגיע להתחלה!‬
‫הפונקציה שבנינו מחזירה רשימה חדשה‪ ,‬ולכן יש צורך להשתמש בה באופן הבא כדי לעדכן את הרשימה‬
‫המקורית‪:‬‬
‫))‪(set! X (update-list X pos val‬‬
‫לדוגמה‪:‬‬
‫))‪(define mylist (list 1 2 3 4‬‬
‫עמוד ‪ | 16‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫))‪(set! myList (update-list myList 0 7‬‬
‫ואז ‪ myList‬ישתנה כך שבמקום הראשון יהיה המספר ‪.7‬‬
‫רשימה באורך ‪ n‬יוצרים בעיקרון רקורסיבי דומה‪.‬‬
‫הטיפול בטבלאות‪ ,‬מושאר לקורא בתור תרגיל‪.‬‬
‫זכרו! טבלה היא רשימה של רשימות‪ ,‬ולכן כדי לגשת לאיבר מסוים‪ ,‬צריך לגשת לאיבר מסוים בשורה‬
‫מסוימת )כאשר היא השורה היא איבר מסוים ברשימה הגדולה שהיא הטבלה(‪.‬‬
‫ישנם סטנדרטים הנהוגים בעת בניית טבלה ורשימה‪ .‬הנה מספר טיפים בנוגע לבנייה שלהם‪ ,‬ולמתן‬
‫השמות של הפונקציות שמתעסקות בטבלאות‪.‬‬
‫‪.1‬‬
‫נהוג לסמן את האינדקס של האיבר הראשון במספר ‪ ,0‬ולא ‪ .1‬תדאגו לכך שכל הפונקציות‬
‫שלכם יתייחסו לאינדקס ‪ 0‬בתור האיבר הראשון – כך תחסכו טעויות אפשריות‪.‬‬
‫‪.2‬‬
‫לפונקציה הניגשת לאיבר מסוים ברשימה כלשהי‪ ,‬בדרך כלל נהוג לתת שם שנגמר בסיומת ‪ref‬‬
‫)מלשון ‪ .(reference‬לדוגמה ‪ table-ref‬יהיה השם הסטנדרטי עבור פונקציה שניגשת לאיבר‬
‫מסוים בטבלה‪ .‬בנוסף‪ ,‬תמיד הערך הראשון שפונקציה כזו תקבל יהיה הרשימה עצמה‪.‬‬
‫‪.3‬‬
‫כאשר מתייחסים לאיבר במקום מסוים בטבלה‪ ,‬נהוג לציין קודם את האינדקס של השורה שלו‬
‫ורק אחר כך את האינדקס של העמודה שלו‪ .‬האינדקס של השורה יסומן ‪ ,row‬ושל העמודה‬
‫יסומן ‪ .col‬הקפידו לשמור על השמות הללו רק עבור משתנים שמייצגים שורה ועמודה‪ ,‬ולא לאף‬
‫מטרה אחרת – זה יעשה את הקוד שלכם קריא וברור יותר‪.‬‬
‫עמוד ‪ | 17‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫פרק ‪ :6‬גרפיקה‪ ,‬ממשק משתמש‪ ,‬מקלדת‪ ,‬ועכבר‬
‫הקובץ ‪ world.ss‬מערכת הלימוד )‪htdp (How To Design Programs‬‬
‫הקדמה – למה להשתמש ב‪?world.ss‬‬
‫לפי הדרישות המינימליות של הפרויקט‪ ,‬אין צורך להציג את הגרפיקה בחלון מיוחד – מספיק להדפיס כל‬
‫פעם תמונה שמציגה את המצב הנוכחי )ובחלק מן המקרים אין כלל צורך בגרפיקה(‪ .‬אבל‪ ,‬זה לא נראה‬
‫טוב במיוחד אם כל פעם רואים גם את כל מה שהיה קודם‪.‬‬
‫הנה השוואה של דרכי הפלט למשתמש של משחק איקס עיגול‪ ,‬כעבור ארבעה מסעים‪:‬‬
‫תרשים ‪ :6.1‬פלט סטנדרטי ע"י הדפסה )מימין(‪ ,‬לעומת הצגה בחלון שמתעדכן כל פעם )משמאל(‬
‫עמוד ‪ | 18‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫‪ world.ss‬מאפשר לנו להגיב לרוב מקשי המקלדת )לא רק לאותיות ומספרים(‪ ,‬לפעולות העכבר‪ ,‬ואפילו‬
‫מאפשר לנו לתזמן פונקציה שתתבצע כל פרק זמן קבוע‪.‬‬
‫על מנת להיות יכולים להשתמש בקובץ ‪ world.ss‬שמכיל את הפונקציות הרצויות‪ ,‬יש להוסיף את אחת‬
‫משתי ההוראות הבאות )תבדקו מה עובד בגרסה שלכם(‪:‬‬
‫)‪(require htdp/world‬‬
‫)"‪(require "collects/htdp/world.ss‬‬
‫במקרה וזה לא עובד‪ ,‬לכו לתיקייה שבה מותקן ‪) scheme‬בד"כ זה בתיקייה ‪(C:\Program Files\PLT‬‬
‫ואז כנסו לתיקייה ‪ ,collects‬אז כנסו לתיקייה ‪ htdp‬ואז בחרו את הקובץ ‪ ,world.ss‬העתיקו אותו ושימו‬
‫אותו באותה תיקייה עם קובץ המשחק שלכם‪ .‬לאחר מכן תוסיפו את ההוראה הבאה לראשית הקובץ‬
‫שלכם‪:‬‬
‫)"‪(require "world.ss‬‬
‫שלב ‪ :1‬עולם )‪(world‬‬
‫עולם )‪ (world‬הוא משתנה כלשהו לפי בחירתנו‪ ,‬אשר מייצג את מידע הקשור במשחק שלנו‪.‬‬
‫לדוגמה‪ :‬אם זה משחק דמקה‪ ,‬אז המשתנה יכול להיות רשימה של טבלה שמייצגת את הלוח‪ ,‬ושל משתנה‬
‫המכיל מידע לגבי תור מי לשחק‪.‬‬
‫בשביל לבנות משחק‪ ,‬נצטרך לבנות מספר פונקציות עיקריות‪:‬‬
‫‪.1‬‬
‫פונקציה שתוכל לצייר תמונה לפי המידע המוכל בעולם שלנו‬
‫‪.2‬‬
‫פונקצית תגובה לקלט מהמשתמש‪ ,‬שתעדכן את העולם בהתאם‪ .‬יש צורך בקלט של עכבר או‬
‫מקלדת או שניהם‪.‬‬
‫‪.3‬‬
‫פונקציה שתעדכן את העולם כל פרק זמן מסוים )רק במקרה שזמן הוא אלמנט במשחק‪ ,‬אחרת‬
‫פונקציה זו אינה חובה(‪.‬‬
‫שלב ‪ :2‬סצנה )‪(scene‬‬
‫על מנת ליצור תמונה שניתן להציג בחלון‪ ,‬יש צורך ליצור תמונה מיוחדת מסוג סצנה )‪ .(scene‬כדי לעשות‬
‫זאת‪ ,‬נשתמש בפונקציה הבאה‪:‬‬
‫)‪(empty-scene width height‬‬
‫כאשר במקום ‪ width‬רשמו את רוחב התמונה בפיקסלים‪ ,‬ובמקום ‪ height‬רשמו את גובהה בפיקסלים‪.‬‬
‫לדוגמה‪:‬‬
‫)‪(empty-scene 50 50‬‬
‫ומה שנקבל זה את התמונה‪:‬‬
‫תרשים ‪ :6.2‬דוגמה לפלט של הפונקציה ‪empty-scene‬‬
‫הבעיה היא שאנו רוצים להציג תמונה משלנו‪ ,‬לכן נעשה את הדבר הבא‪:‬‬
‫)‪(place-image myimage x y myscene‬‬
‫כך נקבל סצנה חדשה שהיא העתק של התמונה ‪ myscene‬אחרי ששמו מעל את התמונה ‪myimage‬‬
‫כאשר מרכזה בנקודה )‪.(x,y‬‬
‫לדוגמה‪:‬‬
‫))‪(define myscene (empty-scene 70 70‬‬
‫עמוד ‪ | 19‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫)‪35 35 myscene‬‬
‫‪(place-image‬‬
‫מה שנקבל זה את התמונה‪:‬‬
‫תרשים ‪ :6.3‬דוגמה לפלט של הפונקציה ‪place-image‬‬
‫טיפים‪:‬‬
‫‪-‬‬
‫שימו לב שמערכת הצירים היא מהפינה השמאלית העליונה כלפי מטה וימינה וזאת בניגוד‬
‫למערכת הצירים אליה אנחנו רגילים משיעורי מתמטיקה‪.‬‬
‫‪x‬‬
‫)‪(0,0‬‬
‫‪y‬‬
‫תרשים ‪ :6.4‬מערכת הצירים של פעולות הגרפיקה‬
‫‪-‬‬
‫שימו לב שתמונה עם אזורים לבנים מפוענחת כאילו האזורים הלבנים הם שקופים ולכן יראו את‬
‫מה שהיה מאחוריהם‪ .‬כדי להימנע מזה‪ ,‬תדאגו שאין אף איזור בתמונה שלכם שהוא בדיוק לבן ‪-‬‬
‫אפשר אפור מאוד בהיר אבל לא לבן )אלא אם אתם רוצים בכוננה שהאיזור יתנהג כאילו הוא‬
‫שקוף(‪.‬‬
‫שלב ‪ :2‬הצגת החלון‬
‫כדי להציג חלון‪ ,‬נשתמש בפונקציה הבאה‪:‬‬
‫)‪(big-bang width height r world0‬‬
‫רוחב התמונה שידע להציג החלון הוא ‪ ,width‬גובה התמונה שידע להציג החלון הוא ‪.height‬‬
‫משך הזמן בין רענון התמונות הוא ‪) r‬בשניות – ולכן עבור ‪ 3‬רענונים בשנייה במקום ‪ r‬נכתוב )‪.((/ 1 3‬‬
‫‪ world0‬הוא משתנה כלשהו שמייצג את העולם ההתחלתי‪.‬‬
‫שלב ‪ :3‬תגובה למקלדת – ‪key-event‬‬
‫)‪(on-key-event change‬‬
‫‪ on-key-event‬זו פונקציה שמקבלת כקלט שם של פונקציה אחרת ‪) change‬גם פונקציות הן סוג של‬
‫משתנים!(‪ ,‬וקוראת לה כל פעם שמתרחש 'אירוע מקלדת' )‪ .(key-event‬הפונקציה ‪ change‬צריכה להיות‬
‫פונקציה שמקבלת שני משתנים בדיוק – העולם הנוכחי )מומלץ לקרוא לו ‪ (active-world‬ואירוע‬
‫המקלדת )מומלץ לקרוא לו ‪ .(key‬הפונקציה ‪ on-key-event‬צריכה להחזיר את העולם המעודכן בהתאם‬
‫ללחיצת המקלדת‪.‬‬
‫המשתנה ‪ key‬יהיה תו )‪ (char‬במידה ונלחץ כפתור ממרכז המקלדת )אות‪ ,‬מספר וכד'( או סימן )‪(symbol‬‬
‫במידה ונלחץ משהו אחר‪.‬‬
‫עמוד ‪ | 20‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫←‬
‫‪Tab‬‬
‫‪Enter‬‬
‫↑‬
‫→ ↓ ←‬
‫‪'numpad5‬‬
‫‪'numpad6‬‬
‫‪'numpad7‬‬
‫‪'numpad8‬‬
‫‪'numpad9‬‬
‫‪Space‬‬
‫‪'numpad0‬‬
‫‪'numpad1‬‬
‫‪'numpad2‬‬
‫‪'numpad3‬‬
‫‪'numpad4‬‬
‫‪'up‬‬
‫‪'down‬‬
‫‪'left‬‬
‫‪'right‬‬
‫הסימן\‪#‬‬
‫‪#\backspace‬‬
‫‪#\space‬‬
‫‪#\return‬‬
‫‪#\tab‬‬
‫תרשים ‪ :6.5‬מקשי המקלדת‬
‫את הפונקציה ‪ change‬מומלץ לבנות כך‪:‬‬
‫)‪(define (change world key‬‬
‫)) הוראות לביצוע ( )‪ key‬מקש ?‪(cond ((equal‬‬
‫…‬
‫)‬
‫)‬
‫לא לשכוח להחזיר את העולם המעודכן!‬
‫שימו לב‪ :‬כאשר המשתמש יעזוב כפתור במקלדת‪ ,‬זה גם נחשב אירוע מקלדת‪ ,‬כאשר הערך שיועבר‬
‫בתור הכפתור שנלחץ יהיה ‪.'released‬‬
‫שלב ‪ :4‬תגובה לעכבר – ‪mouse-event‬‬
‫)‪(on-mouse-event clack‬‬
‫‪ on-mouse-event‬זו פונקציה שמקבלת כקלט שם של פונקציה אחרת ‪ ,clack‬וקוראת לה כל פעם‬
‫שמתרחש 'אירוע עכבר' )‪ .(mouse-event‬הפונקציה ‪ clack‬צריכה להיות פונקציה שמקבלת ארבעה‬
‫משתנים בדיוק – העולם הנוכחי )מומלץ לקרוא לו ‪ (active-world‬מיקום ‪ x‬של העכבר‪ ,‬מיקום ‪ y‬של‬
‫העכבר ואת סוג האירוע‪ .‬סוג האירוע יכול להיות אחד מהבאים‪:‬‬
‫‪ – 'button-down‬נלחץ כפתור‬
‫‪ – 'button-up‬נעזב כפתור‬
‫‪ – 'drag‬גרירה )הזזת העכבר כשיש כפתור לחוץ(‬
‫‪ – 'move‬תנועה )הזזה של העכבר כשאין כפתור לחוץ(‬
‫‪ – 'enter‬כניסה של העכבר לשטח החלון‬
‫‪ - 'leave‬יציאה של העכבר משטח החלון‬
‫גם פה‪ ,‬לא לשכוח להחזיר את העולם המעודכן!‬
‫עמוד ‪ | 21‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫שלב ‪ :5‬תגובה לאירוע זמן ‪tick-event -‬‬
‫)‪(on-tick-event tock‬‬
‫‪ on-tick-event‬זו פונקציה שמקבלת כקלט שם של פונקציה אחרת ‪ ,tock‬וקוראת לה כל ‪ r‬שניות‬
‫)אותו ‪ r‬שהכנסנו בפונקציה ‪ .(big-bang‬שימו לב שהפונקציה ‪ tock‬צריכה להיות פונקציה שמקבלת‬
‫משתנה אחד בלבד – העולם הנוכחי‪ .‬הפונקציה ‪ tock‬צריכה להחזיר את העולם שלנו אחרי העדכון‬
‫המתאים‪.‬‬
‫רוב המשחקים לא זקוקים לפונקציה הזאת‪ ,‬אלא אם כן הם תלויים בזמן‪ .‬לדוגמה‪ ,‬משחק דמקה לא צריך‬
‫עדכון מתוזמן‪ ,‬אבל טטריס כן‪.‬‬
‫שלב ‪ :5‬עדכון התמונה בחלון‬
‫)‪(on-redraw to-scene‬‬
‫‪ on-redraw‬זו פונקציה שמקבלת כקלט שם של פונקציה אחרת ‪ ,to-scene‬וקוראת לה כל פעם לפני‬
‫שהתמונה בחלון מתעדכנת‪ .‬הפונקציה ‪ to-scene‬צריכה להיות פונקציה שקולטת משתנה אחד בלבד ‪-‬‬
‫העולם הנוכחי‪ ,‬ומחזירה סצנה להציג על המסך‪ .‬התמונה במסך מעודכנת כל מספר מקרים‪:‬‬
‫‪.1‬‬
‫כל ‪ r‬שניות )אחרי ‪(tock‬‬
‫‪.2‬‬
‫אחרי כל אירוע מקלדת או אירוע עכבר‪.‬‬
‫שלב ‪ :6‬תנאי סיום‬
‫)?‪(stop-when last-world‬‬
‫‪ stop-when‬זו פונקציה שמקבלת כקלט שם של פונקציה אחרת ?‪ ,last-world‬וקוראת לה כל פעם‬
‫לפני שהתמונה בחלון מתעדכנת‪ .‬הפונקציה ?‪ last-world‬צריכה להיות פונקציה שקולטת משתנה אחד‬
‫בלבד ‪ -‬העולם הנוכחי‪ ,‬ומחזירה ערך בוליאני‪ .‬אם היא מחזירה אמת‪ ,‬אז הפונקציות שמטפלות באירועים‬
‫השונים יפסיקו – לא תהיה עוד תגובה לאירועי זמן‪/‬מקלדת‪/‬עכבר‪ .‬הפונקציה ‪ to-scene‬תמשיך להיקרא‬
‫כל ‪ r‬שניות ותעדכן את המסך‪.‬‬
‫שימו לב‪ :‬פעולה זו היא בלתי הפיכה – מהרגע שעצרתם עולם לא ניתן להמשיך אותו‪.‬‬
‫שלב ‪ :7‬הרצת המשחק‬
‫אחרי שסיימנו להגדיר את כל הפונקציות והמשתנים הדרושים‪ ,‬יש להתחיל את המשחק ע"י הפקודות‬
‫הבאות בסוף הקובץ‪:‬‬
‫)‪(big-bang width height r world0‬‬
‫)‪(on-key-event change‬‬
‫)‪(on-mouse-event clack‬‬
‫)‪(on-tick-event tock‬‬
‫)‪(on-redraw to-scene‬‬
‫)?‪(stop-when last-world‬‬
‫הערות‪:‬‬
‫‪-‬‬
‫הפונקציות המסומנות באדום הן חובה‪.‬‬
‫‪-‬‬
‫יש לקרוא לפחות לאחת מבין הפונקציות שמטפלות באירועים השונים‬
‫‪-‬‬
‫אם לא קוראים לפונקציה ‪ ,stop-when‬הדרך היחידה לעצור את המשחק היא לסגור את החלון‬
‫שלו‪.‬‬
‫‪-‬‬
‫יש לתכנן מראש את גדול החלון – לא ניתן לשנות את גודלו במהלך הריצה‬
‫עמוד ‪ | 22‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫נספח – פונקציות גרפיות‬
‫מידע על תמונות‬
‫מציאת הרוחב של תמונה‬
‫)‪(image-width myimage‬‬
‫מציאת הגובה של תמונה‬
‫)‪(image-height myimage‬‬
‫יצירת משתנה מסוג צבע )‪(color‬‬
‫)‪(make-color red green blue‬‬
‫כאשר ‪ blue ,green ,red‬הם המספרים המייצגים את הצבע )בין ‪ 0‬ל‪.(255‬‬
‫לבן‬
‫)‪(make-color 255 255 255‬‬
‫שחור‬
‫)‪(make-color 0 0 0‬‬
‫אדום‬
‫)‪(make-color 255 0 0‬‬
‫ירוק‬
‫)‪(make-color 0 255 0‬‬
‫כחול‬
‫)‪(make-color 0 0 255‬‬
‫צהוב‬
‫)‪(make-color 255 255 0‬‬
‫תכלת‬
‫)‪(make-color 0 255 255‬‬
‫ורוד‬
‫)‪(make-color 255 0 255‬‬
‫הגדרות מצב ‪ -‬מילוי‪/‬פס חיצוני )‪(mode‬‬
‫‪ – 'solid‬מילוי‪ ,‬ללא קו מתאר‬
‫‪ – 'outline‬קו מתאר‪ ,‬ללא מילוי‬
‫צורות בסיסיות‬
‫אליפסה‬
‫)‪(ellipse width height mode color‬‬
‫מלבן‬
‫)‪(rectangle width height mode color‬‬
‫עיגול‬
‫)‪(circle radius mode color‬‬
‫קו מראשית הצירים לנקודה )‪(x,y‬‬
‫כוכב עם ‪ 2n‬קודקודים עם רדיוס פנימי ‪in-radius‬‬
‫)‪(line x y color‬‬
‫‪(star n in-radius out-radius mode‬‬
‫)‪color‬‬
‫ורדיוס חיצוני ‪out-radius‬‬
‫מצולע משוכלל עם ‪ n‬קודקודים ברדיוס ‪r‬‬
‫)‪(regular-polygon n radius mode color‬‬
‫טקסט‬
‫)‪(text string size color‬‬
‫הטקסט ‪ string‬בגודל ‪ size‬ובצבע ‪color‬‬
‫מיזוג תמונות‬
‫)… ‪(overlay image1 image2‬‬
‫פונקציה זו שמה תמונות אחת מעל השנייה‪ ,‬כאשר המרכזים שלהן באותה נקודה‪ .‬התמונה תתרחב לפי‬
‫הצורך‪.‬‬
‫עמוד ‪ | 23‬המדריך השלם לפרויקט תוכנה ב‪ ,Dr Scheme‬מאת ברק איטקין‬
‫ ומקורות מידע‬,‫קישורים‬
‫ אתר בית הספר בעין גדי‬- ‫חומר בתכנות פונקציונאלי‬
http://www.eingedi.dead-sea.k12.il/scheme1.htm
‫תשתית לבניית משחק לוח – מאתר בית הספר בעין גדי‬
http://www.eingedi.dead-sea.k12.il/scheme21.htm
‫נושאים לעבודות גמר בתכנות פונקציונאלי – מאתר בית הספר בעין גדי‬
http://www.eingedi.dead-sea.k12.il/projects.htm
(‫ )באנגלית‬world.ss ‫חומרים לגבי‬
http://world.cs.brown.edu/
http://world.cs.brown.edu/1/htdw-v1.pdf
http://docs.plt-scheme.org/teachpack/index.html
http://docs.plt-scheme.org/teachpack/world.html
‫ מאת ברק איטקין‬,Dr Scheme‫ | המדריך השלם לפרויקט תוכנה ב‬24 ‫עמוד‬