תכנות פונקציונלי, מודל הסביבה, זרמים, תכנות לוגי ומערכות טיפוסים
proc-val(num-val 6)(num-val 5)(bool-val #t)(bool-val #f)(num-val 12)(num-val 18)(num-val 6)(num-val 9)Expression ::= overload proc [ [identifier]* -> Expression ; ]+
overload-proc-exp(ids, bodies)Expression ::= (Expression | Expression }+)
call-exp(rator, rands)overload proc מגדיר פרוצדורה בשם p שלה מספר גרסאות. כל גרסה כוללת כמות פרמטרים שונה וגוף פרוצדורה שונה לביצוע. הגרסה הראשונה כוללת 2 פרמטרים, הגרסה השנייה היא כפרוצדורה ללא פרמטרים, והגרסה השלישית היא של פרוצדורה בעלת פרמטר יחיד. בגוף פעולת ה-let מופעלת הפרוצדורה p עם 2 פרמטרים, וזה מתאים לגרסה הראשונה שלה, ולכן מבוצעת פעולת החיסור בין הפרמטרים.( ), { }*, { }+.enum בדומה לקיים בשפות תכנות אחרות. לשם כך נרחיב את השפה עם enum כפי שקיים בדוגמה לקיים בשפת RUST (אין צורך בידע מוקדם בשפה זו).enum מורכבת מאוסף של שמות לבחירתו.enum חדש, ביטוי המאפשר להחזיר אחד השמות של enum קיים, וביטוי המאפשר לבחון את ערכי ה-enum.Expression ::= enum { {Identifier}*(,) }
enum-exp (ids)
Expression ::= < Expression . Identifier >
enum-elmt-exp (exm id)
Expression ::= match [ Identifier :: Expression ]
{ ?Identifier => Expression ; }*
match-exp (enmid exp1 enmids exps)
enum. הביטוי בודק אם השמות ה-enum כבר רשומים, ואז יוצר enum חדש ומחזיר אותו.exm ו-id. הוא מחשב את exm ובודק שהתוצאה היא enum. לאחר מכן בודק ש-id הוא אחד הערכים של ה-enum שב-exm. במקרה ש-exm אינו enum, יש להדפיס שגיאה מתאימה. גם אם id אינו חלק מה-enum של exm, יש להדפיס שגיאה מתאימה. אחרת יש להחזיר את ערך ה-id המיוצג.enmid הוא שם של ה-enum שהוגדר בסביבה. ה-exp1 הוא ביטוי שיש לחשב שמחזיר ערך של ה-enum. ה-enmids הוא רשימה של כל הערכים של ה-enum הנתון — על הרשימה להכיל בדיוק את כל הערכים ללא ערכים שלא שייכים אליו. במקרים שאין התאמה מלאה יש להדפיס שגיאה מתאימה. ה-exps הם רשימת הביטויים, אחד לכל ערך, שיש לחשב ולהחזיר את ערכו של הביטוי המתאים ל-exp1.enum:enum בשם Colors המייצג את הצבעים Red Green Blue. לאחר מכן c מקבל לערכו אחד ה-enum המייצג את Green. לאחר מכן מבוצע ביטוי match על c ומוחזר 100 למקרה ה-Green.c מחזיר ערכו. ניתן לראות שהסוג המוחזר הוא identifier-val, שהוא סוג חדש בשפת 'יירשגורי'.match. ניתן לראות שביטוי ה-match חסרה בדיקה לערך Blue ועל כן, שגיאה מוחזרת.enum-elmt-exp, כי i אינו enum (הוא מייצג ערך אחר הקיים בסביבה ההתחלתית).Yellow אינו ערך של Colors ועל כן שגיאה מוחזרת.enum ללא ערכים, ולכן שגיאה מוחזרת.enum המייצג מאכלים שונים, וכן פרוצדורה Calories המקבלת מאכל (מרשימות המאכלים של ה-enum Foods) ומחזירה את כמות הקלוריות שלו. לבסוף מופעלת הפרוצדורה Calories עם הפרמטר Humus (חומוס) וערכה של הפרוצדורה הוא 170.for/proc.Expression ::= for/proc ( identifier ;
| { identifier }*(,) | ;
| { Expression }*(,) | )
{< Guard>}*
{
Expression
}
forproc-exp (Id Ids Bodies Guards Body)
Guard ::= skip : Expression
skip-guard (exp)
Guard ::= break : Expression
break-guard (exp)
forproc הרכיבים הבאים:id ילקח מרשימת ids ויהיה שם הפרמטר בגוף הפרוצדורה שנקבע לפי הביטוי המקביל ברשימת bodies.Guard מסוג שני: skip ו-break. כל Guard מבוטא בביטוי. אם Guard מסוג skip מתקיים (מחזיר אמת), הלולאה מדלגת על האיטרציה הנוכחית וממשיכת לאיטרציה הבאה. ב-Guard מסוג break, כאשר הביטוי מחזיר אמת, הלולאה מסתיימת לאלתר. בכל איטרציה נבדקים כל ה-Guards בסדרם.break מחזיר אמת, או כאשר נגמרים האיברים ברשימות ה-Ids וה-Bodies.forproc-exp אין ערך מוחזר, ולכן יש להחזיר ערך פיקטיבי של num-val 27.w1,w2,w3 המאותחלים לערכים 100,300,500 בהתאמה. לאחר מכן, מבוצעת לולאה for/proc עם משתנה לולאה בשם p. בלולאה יש 4 פרוצדורות (בכל איטרציה נבחרת p עם גוף שונה):p מייצג פרוצדורה בעלת פרמטר a וגוף -(a,1). לא אחד מה-Guards מתקיים, ולכן מבוצע גוף הלולאה. מכיוון שה-p מקבל 1 ומחשב -(1,1)=0, w1 משתנה להיות 7000.p מייצג פרוצדורה בעלת פרמטר b וגוף -(b,2). ה-Guard מסוג skip מתקיים (כי -(2,2)=0), ולכן הלולאה מדלגת לאיטרציה הבאה.p מייצג פרוצדורה עם פרמטר c וגוף -(c,3). אף אחד מה-Guards לא מתקיים, ולכן מבוצע גוף הלולאה. w2 משתנה להיות p (הפרוצדורה הנוכחית).p מייצג פרוצדורה עם פרמטר d וגוף -(d,4). אף אחד מה-Guards לא מתקיים, ולכן מבוצע גוף הלולאה. w3 משתנה להיות (p w1) = (p 7000) = -(7000,4) = 6996.(w2 w3): w2 הוא הפרוצדורה מאיטרציה 3 (פרמטר c, גוף -(c,3)), ומופעלת עם w3=6996, ומחזירה -(6996,3)=6993.break (ולא skip). לכן באיטרציה הראשונה (p עם גוף -(a,1)): Guard ראשון בודק zero?((p 2)) = zero?(-(2,1)) = zero?(1) = שקר — ממשיכים. Guard שני בודק zero?((p 12)) = zero?(-(12,1)) = zero?(11) = שקר — ממשיכים. Guard שלישי בודק zero?((p 200)) = zero?(-(200,1)) = שקר — ממשיכים. מבוצע גוף הלולאה: zero?((p 1)) = zero?(0) = אמת, לכן w1=7000. לאחר מכן ממשיכים לאיטרציה הבאה. לבסוף הלולאה מסתיימת ומחושב -(w1,w2) = -(7000,300) = 6700.forsum-exp המאפשר לגלגל לולאה על רשימת ערכים מספריים, תוך אפשרות לעצירת הלולאה או דילוג על איברים. מרשימת ה-exps מהווה משתנה הלולאה אמור לסקור. מרשימת ה-guards מורכבת מביטויים (guards) יכולים להיות skip או break. הביטוי מחזיר את הסכום של תוצאות הפעולה לכל איבר ברשימה שעליו פעלה הלולאה.Expression ::= for/sum [ identifier ({Expression}*(,)) ]
{ <guard> }*
Expression
for/end
forsum-exp (id exps guards body)
guard ::= skip : Expression skip-guard (boolexp) guard ::= break : Expression break-guard (boolexp)
forsum-exp מורכב מהמרכיבים הבאים:id(run "for/sum [i (1,2,3,20,5,90)]
<skip: zero?(-(i,1))>
<break: zero? (-(i,5))>
<skip: zero? (-(i,3))>
-(i,-5)
for/end")
=> (num-val 32)
(run "for/sum [i ()]
<skip: zero?(-(i,1))>
<break: zero? (-(i,5))>
<skip: zero? (-(i,3))>
-(i,-5)
for/end")
=> interp.scm:79:16: forsum-exp: The list for i is empty
(run "for/sum [i (1,2,3,20,5,90)]
-(i,-5)
for/end")
=> (num-val 151)
do המוגדרת על פי הדקדוק הבא ובאופן המוסבר ומודגם לאחר מכן.do:Expression ::= do (
{ < Identifier Expression Expression > }+
{ [ Expression Expression ] }+
)
do-exp (ids inits steps bools results)
do-exp מגדיר כמות שרירותית (לפחות אחד) של משתנים מספריים (ids), למשתנים יש ערכים התחלתיים הנתונים ע"י הביטויים inits, ובכל מחזור לולאה גדלים הצעדים הנתונים ע"י ביטויים steps.bools), בכל מחזור לולאה נבדקים הביטויים הבוליאניים, והראשון מביניהם שערכו אמת, יגרום ללולאה להיעצר ולהחזיר את ערכו המתאים מתוך ביטוי results המתאים לביטוי הבוליאני שנמצא אמת.do יכול להכיר כבר את המשתנים שכבר אותחלו לפניו.> (run "do(
<a 0 4>
<b 7 -(a,2)>
<c -(b,1) -(a,b)>
[zero? (b) -(a,c)]
[zero? (-(c,-2)) -(a,5)]
)
")
(num-val 3)
>
do המגדירה 3 משתנים a,b,c. בתחילה a שווה לאפס, b שווה ל-7, c שווה ל-1 (6- פחות 1).b הוא 0, תנאי זה אינו מתקיים כרגע. התנאי הבוליאני השני בודק האם ערכו של c הוא 2-, גם הוא לא מתקיים כרגע.a,b,c שערכם הוא כעת:a שווה ל-4, כי היה לו 0 והתווסף לו 4b שווה ל-5, כי היה לו 7 והוא גדל ב-2 (a-2), a במצבו הנוכחי (לפני שקודם) היה 0 ולכן a-2 זה 2-b קטן ב-2 מ-7 לכן 5-c שווה ל-1, כי היה לו 6 והתווסף לו a-b (לפני שקודמו) היה 0 ו-7- בהתאמה ולכן c קטן ב-7 ונהיה 1-a,b,c שערכם הוא:a שווה ל-8, כי היה לו 4 והתווסף לו 4b שווה ל-7, כי היה לו 5, והוא גדל ב-2 (a-2) שזה 2c שווה ל-2, כי היה לו 1- והוא גדל ב-(a-b) שזה 1-a-5 שערכו הנוכחי הוא 8-5=3 ולכן תוצאת התוכנית היא 3.> (run "do(
[zero? (b) -(a,c)]
[zero? (-(c,-2)) -(a,5)]
)
")
interp.scm:72:15: do-exp: do loop with no variables are not allowed
>
do ללא הגדרת משתנים, על פי הדקדוק הנתון בשאלה נדרש לפחות משתנה אחד ולכן התקבלה הודעת שגיאה מתאימה.> (run "do(
<a 0 4>
<b 7 -(a,2)>
<c -(b,1) -(a,b)>
)
")
interp.scm:74:19: do-exp: do loop with no booleans are not allowed
>
do ללא הגדרת תנאים בוליאניים, על פי הדקדוק הנתון בשאלה נדרש לפחות תנאי בוליאני אחד ולכן התקבלה הודעת שגיאה מתאימה.value-of או כאלה המוטמעות בתוכו).expression) לבין תוצאת חישוב ביטוי (expval) לבין identifier.)* או { }+).(proc (x : !) zero?(x) let z=7 in z)
Expression ::= try { Expression }
{ catch [ Except ] : Expression ; }*
{ finally : Expression ; }*
try-exp (exp1 excpts excptexps finexps)Expression ::= throw Except
throw-exp (excpt)Except ::= message Exception (msg) message ::= general | not a number | not a boolean | environment
exp1 הוא הביטוי בבלוק ה-try.excpts הוא רשימת החריגות שב-catch; החריגה הראשונה ברשימה המתאימה לחריגה שנזרקה תטופל.excptexps הוא רשימת הביטויים המתאימים לחריגות ב-catch.finexps הוא הביטוי ב-finally (רשימה ריקה או אחת); מבוצע תמיד בסוף.value-of או כאלה המוטמעות בתוכו).const, כלומר לאחר קביעת ערכם, הם לא ניתנים לשינוי.let ומגדירים במסגרתו מתחדש בתוך הפרוצדורה, שבו הפעלת הפרוצדורה בתהליך ההפעלה מתווסף פרמטר הפרוצדורה לסביבה עמה מחשבים את גוף הפרוצדורה, ובה הוא מקבל את ערכו של הארגומנט הנשלח בביטויי ההפעלה.const, כלומר, רק אם המתכנת שהשתמש במתכונת המוגדרת בשפה יהיה כ-const אז לא ניתן יהיה לשנות את ערכו. אך אם לא היה שימוש באופציה של const אזי המשתנה ניתן לשינוי יותר.Expression ::= let option Identifier = Expression
in Expression
let-exp (opt id exp1 body)Expression ::= ( Expression option Expression )
call-exp (rator opt rand)option ::= ε | const
option היא הריקה, let-exp ו-call-exp מתנהגים כרגיל.option היא const:let-exp: לא ניתן יהיה לשנות את ערכו של id במסגרת ה-body של ה-let; אם יהיה ניסיון כזה יש להדפיס שגיאה מתאימה.call-exp: לפני הארגומנט הנשלח (rand) ניתן לציין את המילה const או לא לציין דבר; אם כן, לא ניתן יהיה לשנות את ערכו של פרמטר הפרוצדורה המופעלת במסגרת ביצוע גוף הפרוצדורה, וגם אם יהיה ניסיון שינוי שם יש להדפיס שגיאה מתאימה.value-of או כאלה המוטמעות בתוכו).שאלות תרגול בשפות תכנות בנושא תכנות פונקציונלי ממבחנים של האוניברסיטה הפתוחה. השאלות ממוינות לפי רמת קושי, עם פתרונות לבדיקה עצמית - כך שניתן ללמוד מטעויות ולהבין את דרך הפתרון לפני הבחינה. ניתן לאתר שאלות חוזרות ולהריץ מבחני סימולציה ממוקדים בנושא תכנות פונקציונלי.