המדריך השלם לפרויקט תוכנה ב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 עמוד
© Copyright 2024