הבנה מודרנית של רקורסיה: הגדרת פונקציונליות וגישה אליה מבחוץ ומתוך פונקציונליות זו. מאמינים כי הרקורסיה נולדה על ידי מתמטיקאים: חישוב פקטוריאלי, סדרות אינסופיות, פרקטלים, שברים מתמשכים… עם זאת, ניתן למצוא רקורסיה בכל מקום. חוקי טבע אובייקטיביים "רואים" את הרקורסיה כאלגוריתם וצורת הביטוי (הקיום) העיקריים שלהם, לא כל כך מהאובייקטים של העולם החומרי, אלא באופן כללי באלגוריתם העיקרי של התנועה.
אנשים בעלי התמחויות שונות בתחומים שונים של מדע וטכנולוגיה משתמשים באלגוריתם הרקורסי f (x), שבו "x ~/=f (x)". פונקציה שקוראת לעצמה היא פתרון חזק, אבל יצירת והבנה של פתרון זה היא, ברוב המקרים, משימה קשה מאוד.
בימי קדם, הרקורסיה שימשה להגדלת שטח הארמון. באמצעות מערכת של מראות המכוונות זו לזו, תוכלו ליצור אפקטים מרחביים תלת מימדיים מדהימים. אבל האם זה כל כך קל להבין איךלהתאים את המראות האלה? קשה עוד יותר לקבוע היכן נמצאת נקודה בחלל, המשתקפת דרך מספר מראות.
רקורסיה, אלגוריתמים רקורסיביים: משמעות ותחביר
הבעיה, שמנוסחת על ידי חזרה על רצף פעולות, ניתנת לפתרון רקורסיבית. אלגוריתם פשוט (חישוב משוואה ריבועית, סקריפט לאכלוס דף אינטרנט במידע, קריאת קובץ, שליחת הודעה…) אינו דורש רקורסיה.
הבדלים עיקריים של האלגוריתם המאפשר פתרון רקורסיבי:
- יש אלגוריתם שצריך להפעיל מספר פעמים;
- algorithm זקוק לנתונים המשתנים בכל פעם;
- האלגוריתם לא חייב להשתנות בכל פעם;
- יש תנאי סופי: האלגוריתם הוא רקורסיבי - לא אינסופי.
באופן כללי, לא ניתן לטעון שביצוע חד פעמי הוא תנאי הכרחי להעדר סיבה לרקורסיה. אתה גם לא יכול לדרוש תנאי סופי חובה: לרקורסיות אינסופיות יש היקף משלהן.
האלגוריתם הוא רקורסיבי: כאשר רצף של פעולות מתבצע שוב ושוב, על נתונים המשתנים בכל פעם ונותנים תוצאה חדשה בכל פעם.
נוסחת רקורסיה
ההבנה המתמטית של הרקורסיה והאנלוגי שלה בתכנות שונים. מתמטיקה, אמנם יש סימנים של תכנות, אבל תכנות היא מתמטיקה מסדר גבוה בהרבה.
אלגוריתם כתוב היטב הוא כמו מראה לאינטלקט של מחברו. כללינוסחת הרקורסיה בתכנות היא "f(x)", כאשר ל-"x ~/=f(x)" יש לפחות שתי פירושים. כאן "~" הוא הדמיון או היעדר התוצאה, ו-"=" הוא הנוכחות של התוצאה של הפונקציה.
אפשרות ראשונה: דינמיקת נתונים.
- לפונקציה "f(x)" יש אלגוריתם רקורסיבי ובלתי ניתן לשינוי;
- ל-"x" ולתוצאה "f(x)" יש ערכים חדשים בכל פעם, התוצאה "f(x)" היא הפרמטר "x" החדש של פונקציה זו.
אפשרות שנייה: דינמיקת קוד.
- לפונקציה "f(x)" יש כמה אלגוריתמים שמעדנים (מנתחים) את הנתונים;
- ניתוח נתונים - חלק אחד של הקוד והטמעת אלגוריתמים רקורסיביים המבצעים את הפעולה הרצויה - החלק השני של הקוד;
- התוצאה של הפונקציה "f(x)" אינה.
אין תוצאה תקינה. תכנות זה לא מתמטיקה, כאן התוצאה לא חייבת להיות נוכחת במפורש. פונקציה רקורסיבית יכולה פשוט לנתח אתרים ולאכלס את מסד הנתונים, או ליצור אובייקטים לפי הקלט הנכנס.
נתונים ורקורסיה
תכנות אלגוריתמים רקורסיביים לא עוסק בחישוב פקטוריאלי, שבו הפונקציה מקבלת בכל פעם ערך נתון שהוא אחד יותר או פחות מאחד - אפשרות היישום תלויה בהעדפת המפתח.
זה לא משנה איך ה-"8!" הפקטוריאלי,אלגוריתם שעוקב אחר נוסחה זו.
עיבוד מידע הוא "מתמטיקה" בסדר אחר לגמרי. פונקציות ואלגוריתמים רקורסיביים פועלים כאן על אותיות, מילים, ביטויים, משפטים ופסקאות. כל רמה הבאה משתמשת בקודמתה.
זרם נתוני הקלט מנותח על פני מגוון רחב של תנאים, אך תהליך הניתוח הוא בדרך כלל רקורסיבי. אין זה הגיוני לכתוב אלגוריתמים ייחודיים עבור כל הווריאציות של זרם הקלט. צריכה להיות פונקציונליות אחת. כאן, אלגוריתמים רקורסיביים הם דוגמאות כיצד ליצור זרם פלט שמתאים לקלט. זה לא הפלט של האלגוריתם הרקורסי, אבל זה הפתרון הרצוי וההכרחי.
אבסטרקציה, רקורסיה ו-OOP
תכנות מונחה-אובייקט (OOP) ורקורסיה הן ישויות שונות מהותית, אך הן משלימות זו את זו בצורה מושלמת. להפשטה אין שום קשר לרקורסיה, אבל דרך העדשה של OOP היא יוצרת אפשרות ליישם רקורסיה הקשרית.
לדוגמה, מידע מנותח ואותיות, מילים, ביטויים, משפטים ופסקאות מודגשים בנפרד. ברור שהמפתח ידאג ליצירת מופעים של אובייקטים מחמשת הסוגים הללו ויציע פתרון של אלגוריתמים רקורסיביים בכל רמה.
בינתיים, אם ברמת האותיות "אין טעם לחפש משמעות", אז סמנטיקה מופיעה ברמת המילים. אפשר לחלק מילים לפעלים, שמות עצם, פתגמים, מילות יחס… אפשר ללכת רחוק יותר ולהגדיר מקרים.
ברמת הביטוי, הסמנטיקה מתווספת על ידי סימני פיסוק והיגיוןצירופי מילים. ברמת המשפטים, נמצא רמה מושלמת יותר של סמנטיקה, ופסקה יכולה להיחשב כמחשבה שלמה.
פיתוח מונחה-אובייקט קובע מראש את הירושה של מאפיינים ושיטות ומציע להתחיל את ההיררכיה של אובייקטים עם יצירת אב קדמון מופשט לחלוטין. יחד עם זאת, ללא ספק, הניתוח של כל צאצא יהיה רקורסיבי ולא יהיה שונה מדי ברמה הטכנית בעמדות רבות (אותיות, מילים, ביטויים ומשפטים). פסקאות, כמו מחשבות שלמות, עשויות לבלוט מהרשימה הזו, אבל הן לא המהות.
חשוב שניתן יהיה לנסח את החלק המכריע של האלגוריתם ברמת האב הקדמון המופשט, ולחדד אותו ברמה של כל צאצא עם נתונים ושיטות הנקראים מהרמה המופשטת. בהקשר זה, הפשטה פותחת אופקים חדשים לרקורסיה.
מאפיינים היסטוריים של OOP
OOP הגיע לעולם התוכנה פעמיים, אם כי חלק מהמומחים עשויים להבחין בהופעתו של מחשוב ענן ורעיונות מודרניים לגבי אובייקטים ומחלקות כסיבוב חדש בפיתוח טכנולוגיות IT.
המונחים "אובייקט" ו"אובייקטיבי" בהקשר המודרני של OOP מיוחסים בדרך כלל לשנות ה-50 וה-60 של המאה הקודמת, אך הם קשורים ל-1965 ולהופעתם של Simula, Lisp, Algol, Smalltalk.
באותם ימים, התכנות לא היה מפותח במיוחד ולא יכול היה להגיב בצורה מספקת למושגים מהפכניים. מאבק הרעיונות וסגנונות התכנות (C/C++ ופסקל - בעיקר) היה עדיין רחוק, ומסדי נתונים עדיין נוצרו קונספטואלית.
בסוף שנות ה-80 ותחילת שנות ה-90, אובייקטים הופיעו בפסקל וכולם זכרו שיעורים ב-C/C++ - זה סימן סבב חדש של התעניינות ב-OOP ואז הכלים, בעיקר שפות תכנות, הפכו לא תומכים רק ברעיונות מונחה עצמים, אך מתפתחים בהתאם.
כמובן, אם אלגוריתמים רקורסיביים קודמים היו רק פונקציות ששימשו בקוד הכללי של התוכנית, כעת הרקורסיה יכולה להפוך לחלק מהמאפיינים של אובייקט (מחלקה), מה שסיפק הזדמנויות מעניינות בהקשר של ירושה.
תכונה של OOP מודרני
הפיתוח של OOP הכריז בתחילה על אובייקטים (מחלקות) כאוספים של נתונים ומאפיינים (שיטות). למעשה, זה היה על נתונים שיש להם תחביר ומשמעות. אבל אז לא ניתן היה להציג את OOP ככלי לניהול אובייקטים אמיתיים.
OOP הפך לכלי לניהול אובייקטים של "טבע המחשב". סקריפט, כפתור, פריט תפריט, שורת תפריטים, תג בחלון דפדפן הוא אובייקט. אבל לא מכונה, מוצר מזון, מילה או משפט. אובייקטים אמיתיים נותרו מחוץ לתכנות מונחה עצמים, וכלי מחשב קיבלו גלגול חדש.
בשל ההבדלים בשפות התכנות הפופולריות, הופיעו דיאלקטים רבים של OOP. מבחינת סמנטיקה, הם כמעט שוות ערך, וההתמקדות שלהם בספירה האינסטרומנטלית, ולא במיושמת, מאפשרת לקחת את התיאור של אובייקטים אמיתיים מעבר.אלגוריתמים ומבטיחים את ה"קיום" בין הפלטפורמות והשפות שלהם.
מחסניות ומנגנוני קריאת פונקציות
מנגנונים לקריאה לפונקציות (פרוצדורות, אלגוריתמים) מחייבים העברת נתונים (פרמטרים), החזרת תוצאה וזכירת כתובת המפעיל שחייב לקבל שליטה לאחר סיום הפונקציה (הפרוצדורה).
בדרך כלל, המחסנית משמשת למטרה זו, למרות ששפות תכנות או המפתח עצמו יכולים לספק מגוון אפשרויות להעברת שליטה. התכנות המודרני מודה ששם של פונקציה יכול להיות לא רק פרמטר: הוא יכול להיווצר במהלך ביצוע האלגוריתם. ניתן ליצור אלגוריתם גם תוך כדי ביצוע אלגוריתם אחר.
המושג של אלגוריתמים רקורסיביים, כאשר ניתן לקבוע את שמם וגופם בזמן היווצרות המשימה (בחירת האלגוריתם הרצוי), מרחיב רקורסיביות לא רק איך לעשות משהו, אלא גם למי בדיוק צריך תעשה את זה. בחירת אלגוריתם בשמו ה"משמעותי" מבטיחה, אך יוצרת קשיים.
רקורסיביות על סט פונקציות
אי אפשר לומר שאלגוריתם הוא רקורסיבי כשהוא קורא לעצמו וזהו. תכנות אינו דוגמה, והמושג רקורסיביות אינו דרישה בלעדית לקרוא לעצמך מגוף האלגוריתם שלך.
יישומים מעשיים לא תמיד נותנים פתרון נקי. לעתים קרובות, יש להכין את הנתונים הראשוניים, ולנתח את התוצאה של הקריאה הרקורסיבית בהקשר של הבעיה כולה (האלגוריתם כולו) בבסך הכל.
למעשה, לא רק לפני קריאה לפונקציה רקורסיבית, אלא גם לאחר השלמתה, אפשר או צריך לקרוא לתוכנית אחרת. אם אין בעיות מיוחדות בקריאה: הפונקציה הרקורסיבית A() קוראת לפונקציה B(), שעושה משהו וקוראת ל-A(), אז מיד יש בעיה בהחזרת השליטה. לאחר השלמת הקריאה הרקורסיבית, הפונקציה A() חייבת לקבל שליטה על מנת לקרוא מחדש ל-B(), שתקרא לה שוב. החזרת השליטה כפי שהיא צריכה להיות בסדר בערימה בחזרה ל-B() היא הפתרון השגוי.
המתכנת אינו מוגבל בבחירת הפרמטרים ויכול להשלים אותם עם שמות פונקציות. במילים אחרות, הפתרון האידיאלי הוא להעביר את השם של B() ל-A() ולתת ל-A() עצמו לקרוא ל-B(). במקרה זה, לא יהיו בעיות עם החזרת שליטה, והיישום של האלגוריתם הרקורסי יהיה שקוף יותר.
הבנה ורמת הרקורסיה
הבעיה בפיתוח אלגוריתמים רקורסיביים היא שאתה צריך להבין את הדינמיקה של התהליך. כאשר משתמשים ברקורסיה בשיטות אובייקט, במיוחד ברמה של אב קדמון מופשט, יש בעיה בהבנת האלגוריתם שלך בהקשר של זמן הביצוע שלו.
נכון לעכשיו, אין הגבלות על רמת הקינון של פונקציות וקיבולת מחסנית במנגנוני קריאה, אבל ישנה בעיה של הבנה: באיזו נקודת זמן איזו רמת נתונים או איזה מקום באלגוריתם הכללי נקרא רקורסיבי פונקציה ובאיזה מספר שיחות היא עצמה.
כלי ניפוי באגים קיימים לרוב חסרי אוניםספר למתכנת את הפתרון הנכון.
לולאות ורקורסיה
זה נחשב שביצוע מחזורי שווה ערך לרקורסיה. ואכן, במקרים מסוימים, ניתן ליישם את האלגוריתם הרקורסי בתחביר של מבנים מותנים ומחזוריים.
עם זאת, אם ישנה הבנה ברורה שיש ליישם פונקציה מסוימת באמצעות אלגוריתם רקורסיבי, יש לנטוש כל שימוש חיצוני בלולאה או בהצהרות מותנות.
הכוונה כאן היא שפתרון רקורסיבי בצורת פונקציה המשתמשת בעצמה יהיה אלגוריתם שלם ושלם מבחינה תפקודית. אלגוריתם זה ידרוש מהמתכנת ליצור אותו במאמץ, תוך הבנת הדינמיקה של האלגוריתם, אך הוא יהיה הפתרון הסופי שאינו דורש שליטה חיצונית.
כל שילוב של אופרטורים מותנים ומחזוריים חיצוניים לא יאפשר לנו לייצג את האלגוריתם הרקורסי כפונקציה שלמה.
קונצנזוס רקורסי ו-OOP
כמעט בכל הגרסאות של פיתוח אלגוריתם רקורסיבי, עולה תוכנית לפיתוח שני אלגוריתמים. האלגוריתם הראשון יוצר רשימה של אובייקטים עתידיים (מופעים), והאלגוריתם השני הוא למעשה פונקציה רקורסיבית.
הפתרון הטוב ביותר יהיה לארגן את הרקורסיה כמאפיין (שיטה) יחיד שמכיל למעשה את האלגוריתם הרקורסי, ולהכניס את כל עבודת ההכנה לבונה האובייקטים.
אלגוריתם רקורסיבי יהיה הפתרון הנכון רק כשהוא עובדרק בעצמו, ללא בקרה וניהול חיצוניים. אלגוריתם חיצוני יכול רק לתת אות לעבודה. התוצאה של עבודה זו צריכה להיות הפתרון המצופה, ללא תמיכה חיצונית.
רקורסיה תמיד צריכה להיות פתרון שלם עצמאי.
הבנה אינטואיטיבית ושלמות תפקודית
כאשר תכנות מונחה עצמים הפך לסטנדרט דה פקטו, היה ברור שכדי לקוד ביעילות, אתה צריך לשנות את החשיבה שלך. על המתכנת לעבור מהתחביר והסמנטיקה של השפה לדינמיקה של הסמנטיקה במהלך ביצוע האלגוריתם.
אופייני לרקורסיה: ניתן להחיל אותה על הכל:
- גרידת אינטרנט;
- פעולות חיפוש;
- ניתוח מידע טקסט;
- קריאה או יצירה של מסמכי MS Word;
- דגימה או ניתוח תגים…
מאפיין של OOP: הוא מאפשר לתאר אלגוריתם רקורסיבי ברמה של אב קדמון מופשט, אך מספקים לו התייחסות לצאצאים ייחודיים, שלכל אחד מהם יש פלטת נתונים ומאפיינים משלו.
רקורסיה היא אידיאלית מכיוון שהיא דורשת את השלמות הפונקציונלית של האלגוריתם שלה. OOP משפר את הביצועים של אלגוריתם רקורסיבי על ידי מתן גישה לכל הילדים הייחודיים.