Πώς να δημιουργήσετε μια διαίσθηση για την επανάληψη

Και πώς να το χρησιμοποιήσετε για την επίλυση προβλημάτων

Η επανάληψη είναι ένα από τα πιο εκφοβιστικά θέματα που αντιμετωπίζουν οι φοιτητές στον προγραμματισμό. Είναι δύσκολο να καταλάβει κανείς γιατί ο ανθρώπινος εγκέφαλος δεν είναι σε θέση να κάνει επανάληψη - αλλά οι υπολογιστές είναι. Αυτός ακριβώς είναι ο λόγος για τον οποίο η αναδρομή είναι ένα τόσο ισχυρό εργαλείο για τους προγραμματιστές, αλλά σημαίνει επίσης ότι η εκμάθηση πώς να το χρησιμοποιήσετε είναι εξαιρετικά δύσκολη. Θέλω να σας βοηθήσω να δημιουργήσετε μια διαίσθηση για την επανάληψη, ώστε να μπορείτε να την χρησιμοποιήσετε για την επίλυση προβλημάτων.

Είμαι βοηθός διδασκαλίας για το εισαγωγικό μάθημα πληροφορικής στο πανεπιστήμιο μου. Έχω εξηγήσει την επανάληψη με τον ίδιο ακριβώς τρόπο δεκαπλάσια αυτή την εβδομάδα. Η εξήγησή μου φαίνεται να βοηθά τους περισσότερους φοιτητές. Αυτό το άρθρο έχει την πιο γενική εξήγηση στην κορυφή και την πιο συγκεκριμένη εξήγηση στο κάτω μέρος. Με αυτόν τον τρόπο, μπορείτε να ξεκινήσετε από την αρχή και να σταματήσετε μόλις αισθανθείτε ότι καταλαβαίνετε επαρκώς την υποτροπή. Έχω δώσει κάποια παραδείγματα στη Java και είναι αρκετά απλά που κάποιος με κάποια εμπειρία προγραμματισμού μπορεί να τα ερμηνεύσει.

Τι είναι η Επανάληψη;

Για να κατανοήσουμε την επανάληψη, ας κάνουμε ένα βήμα πίσω από τον προγραμματισμό. Ας ξεκινήσουμε καθορίζοντας έναν γενικό ορισμό του όρου. Κάτι είναι αναδρομικό εάν ορίζεται από τον ορισμό του σε κάποιο βαθμό. Αυτό πιθανώς δεν σας βοηθά να κατανοήσετε πολύ καλά την αναδρομή, οπότε ας δούμε έναν μαθηματικό ορισμό. Είστε εξοικειωμένοι με λειτουργίες - ένας αριθμός εισέρχεται, ένας άλλος αριθμός βγαίνει. Μοιάζουν με αυτό:

f (x) = 2x

Ας αλλάξουμε αυτή την ιδέα ελαφρώς και αντ 'αυτού να σκεφτούμε μια ακολουθία. Μια ακολουθία παίρνει έναν ακέραιο αριθμό και ένας ακέραιος αριθμός βγαίνει.

A (n) = 2n

Οι ακολουθίες μπορούν να θεωρηθούν ως λειτουργίες με εισόδους και εξόδους που περιορίζονται μόνο σε θετικούς ακέραιους αριθμούς. Γενικά, οι ακολουθίες αρχίζουν με 1. Αυτό σημαίνει ότι το Α (0) είναι 1. Η παραπάνω ακολουθία είναι η ακόλουθη:

A (n) = 1, 2, 4, 6, 8, 10, ... όπου n = 0, 1, 2, 3, 4, 5, ...

Τώρα, σκεφτείτε την ακόλουθη ακολουθία:

Α (η) = 2 χ Α (η-1)

Αυτή η ακολουθία ορίζεται αναδρομικά. Με άλλα λόγια, η τιμή κάθε δεδομένου στοιχείου εξαρτάται από την τιμή ενός άλλου στοιχείου. Αυτή η ακολουθία μοιάζει με αυτό:

A (n) = 1, 2, 4, 8, 16, ... όπου n = 0, 1, 2, 3, 4, ...

Οποιοδήποτε στοιχείο ορίζεται ως 2 φορές το προηγούμενο στοιχείο.

  • Το στοιχείο n = 4, 16, ορίζεται ως 2 φορές το προηγούμενο στοιχείο.
  • Το στοιχείο n = 3, 8, ορίζεται ως 2 φορές το προηγούμενο στοιχείο.
  • Το στοιχείο n = 2, 4, ορίζεται ως 2 φορές το προηγούμενο στοιχείο.
  • Το στοιχείο n = 1, 2, ορίζεται ως 2 φορές το προηγούμενο στοιχείο.
  • Το στοιχείο n = 0, 1, ορίζεται ως ...

Το στοιχείο n = 0 δεν μπορεί να οριστεί αναδρομικά. Δεν υπάρχει προηγούμενο στοιχείο. Το ονομάζουμε μια βασική περίπτωση και είναι μια απαραίτητη συνέπεια αναδρομικών ορισμών. Πρέπει να εκπροσωπούνται ρητά στον κώδικα σας. Θα μπορούσαμε να αντιπροσωπεύσουμε αυτή την αναδρομική αλληλουχία στην Java όπως έτσι:

δημόσιο int A (int n)
{
    αν (n == 0)
        επιστροφή 1;
    επιστροφή 2 * Α (n - 1);
}}

Θα πρέπει να εξοικειωθείτε με την ανατομία μιας αναδρομικής μεθόδου. Σημειώστε την βασική περίπτωση: αν το n είναι 0, το στοιχείο ορίζεται ως 1. Διαφορετικά, το στοιχείο ορίζεται ως 2 φορές το προηγούμενο στοιχείο. Πρέπει να καλέσουμε αναδρομικά τη μέθοδο για να πάρουμε την τιμή του προηγούμενου στοιχείου και στη συνέχεια να την πολλαπλασιάσουμε με 2. Όλες οι αναδρομικές μέθοδοι θα έχουν αυτά τα δύο στοιχεία:

  • Θήκη βάσης, η οποία επιστρέφει μια καλά καθορισμένη τιμή.
  • Αναδρομική περίπτωση, η οποία επιστρέφει μια επαναλαμβανόμενα καθορισμένη τιμή.

Ας κάνουμε ένα άλλο παράδειγμα, συνεχίζοντας με το μαθηματικό πλαίσιο. Η αλληλουχία Fibonacci χρησιμοποιείται συχνά για να απεικονίσει την υποτροπή. Οποιοδήποτε στοιχείο της ακολουθίας Fibonacci είναι το άθροισμα των δύο προηγούμενων στοιχείων. Πάει κάπως έτσι:

F (n) = 1, 1, 2, 3, 5, 8, ... όπου n = 0, 1, 2, 3, 4, 5, ...

  • Το στοιχείο n = 5, 8, ορίζεται ως το άθροισμα του στοιχείου n = 4 και του στοιχείου n = 3 ...

Σε αυτό το σημείο, θα πρέπει να διστάσετε. Στο προηγούμενο παράδειγμα, κάθε στοιχείο εξαρτιόταν μόνο από ένα άλλο στοιχείο, τώρα κάθε στοιχείο εξαρτάται από δύο άλλα στοιχεία. Αυτό περιπλέκει τα πράγματα.

  • Το στοιχείο n = 4, 5, ορίζεται ως το άθροισμα του στοιχείου n = 3 και του στοιχείου n = 2.
  • Το στοιχείο n = 3, 3, ορίζεται ως το άθροισμα του στοιχείου n = 2 και του στοιχείου n = 1.
  • Το στοιχείο n = 2, 2, ορίζεται ως το άθροισμα του στοιχείου n = 1 και του στοιχείου n = 0.
  • Το στοιχείο n = 1, 1, ορίζεται ως το άθροισμα του στοιχείου n = 0 και ...

Το στοιχείο n = 1 δεν μπορεί να οριστεί αναδρομικά. Ούτε το στοιχείο n = 0. Αυτά τα στοιχεία δεν μπορούν να οριστούν αναδρομικά επειδή ο αναδρομικός ορισμός απαιτεί δύο προηγούμενα στοιχεία. Το στοιχείο n = 0 δεν έχει προηγούμενα στοιχεία και το στοιχείο n = 1 έχει μόνο ένα προηγούμενο στοιχείο. Αυτό σημαίνει ότι υπάρχουν δύο βασικές περιπτώσεις. Πριν γράψω κάποιο κώδικα, θα γράψω κάτι σαν αυτό:

Το στοιχείο n = 0 ορίζεται ως 1. Το στοιχείο n = 1 ορίζεται ως 1.

Το στοιχείο n ορίζεται ως το άθροισμα του στοιχείου n-1 και του στοιχείου n-2.

Τώρα έχουμε μια ιδέα για το πώς αυτό το καθήκον ορίζεται αναδρομικά, και μπορούμε να προχωρήσουμε και να γράψουμε κάποιο κώδικα. Ποτέ μην ξεκινάτε να γράφετε κώδικα χωρίς πρώτα να έχετε φυσική κατανόηση της εργασίας.

δημόσιο int F (int n)
{
    αν (n == 0 || n == 1)
        επιστροφή 1;
    επιστροφή F (n - 1) + F (n - 2).
}}

Η Στοίβα κλήσεων

Ως προγραμματιστές, θέλουμε να έχουμε μια διαίσθηση για την επανάληψη έτσι ώστε να μπορούμε να την χρησιμοποιήσουμε για να κάνουμε πράγματα. Για να γίνει αυτό αποτελεσματικά, πρέπει να καταλάβουμε πώς ένας υπολογιστής επεξεργάζεται την υποτροπή.

Υπάρχει μια δομή δεδομένων που χρησιμοποιεί ο υπολογιστής για να παρακολουθεί τις κλήσεις μεθόδου που ονομάζεται στοίβα κλήσεων. Κάθε κλήση μεθόδου δημιουργεί τοπικές μεταβλητές από τις παραμέτρους μεθόδου. Ο υπολογιστής πρέπει να αποθηκεύσει αυτές τις μεταβλητές κατά την εκτέλεση της μεθόδου. Στη συνέχεια, ο υπολογιστής σπρώχνει τις τιμές όταν η μέθοδος επιστρέφει για να αποφευχθεί η σπατάλη μνήμης.

Η στοίβα κλήσεων (και οι στοίβες εν γένει) λειτουργούν όπως θα φανταστείτε ότι θα υπήρχε κάποια στοίβα πραγματικής ζωής. Φανταστείτε μια στοίβα χαρτιών στο γραφείο σας - αρχίζει ως τίποτα, και στη συνέχεια προσθέτετε έγγραφα μία προς μία. Δεν γνωρίζετε τίποτα για κανένα από τα χαρτιά στη στοίβα εκτός από το χαρτί που βρίσκεται πάνω. Ο μόνος τρόπος για να αφαιρέσετε τα χαρτιά από τη στοίβα είναι να τα βγάλετε από την κορυφή, ένα προς ένα, με την αντίθετη σειρά που είχαν προστεθεί.

Αυτός είναι ουσιαστικά ο τρόπος με τον οποίο λειτουργεί η στοίβα κλήσεων, εκτός από τα στοιχεία της στοίβας που είναι εγγραφές ενεργοποίησης αντί για χαρτιά. Οι εγγραφές ενεργοποίησης είναι μόνο μικρά κομμάτια δεδομένων που αποθηκεύουν το όνομα της μεθόδου και τις τιμές των παραμέτρων.

Χωρίς επανάληψη, η στοίβα κλήσεων είναι αρκετά απλή. Ακολουθεί ένα παράδειγμα. Αν είχατε κάποιο κωδικό που έμοιαζε με αυτό ...

δημόσιο στατικό κενό κύρια (String [] args)
    System.out.println (myMethod (1)).

... Η στοίβα κλήσεων θα φαίνεται ως εξής:

* myMethod (int a)
* main (String [] args)

Εδώ βλέπουμε δύο μεθόδους υπό εκτέλεση, main και myMethod. Το σημαντικό πράγμα που πρέπει να παρατηρήσετε είναι ότι το main δεν μπορεί να αφαιρεθεί από τη στοίβα μέχρι να αφαιρεθεί το myMethod από τη στοίβα. Με άλλα λόγια, το κύριο δεν μπορεί να ολοκληρωθεί μέχρι να καλέσει, να εκτελέσει το myMethod και να επαναφέρει μια τιμή.

Αυτό ισχύει για κάθε περίπτωση σύνθεσης μεθόδου (μέθοδος μέσα σε μια μέθοδο) - οπότε ας δούμε αναδρομικό παράδειγμα: τη μέθοδο A (int n) που γράψαμε προηγουμένως. Ο κωδικός σας μπορεί να μοιάζει με αυτόν:

δημόσιο στατικό κενό κύρια (String [] args)
    System.out.println (Α (4)).
δημόσιο στατικό int A (int n)
{
    αν (n == 0)
        επιστροφή 1;
    επιστροφή 2 * Α (n - 1);
}}

Όταν το κύριο καλείται, ονομάζεται Α. Όταν ονομάζεται Α, αποκαλείται. Έτσι, η στοίβα κλήσεων θα ξεκινήσει να φτιάχνει έτσι:

* Α (4)
* main (String [] args)

Το Α (4) καλεί το Α (3).

* Α (3)
* Α (4)
* main (String [] args)

Τώρα, είναι σημαντικό να σημειωθεί ότι το A (4) δεν μπορεί να αφαιρεθεί από τη στοίβα κλήσεων μέχρι να αφαιρεθεί A (3) από τη στοίβα κλήσεων πρώτα. Αυτό έχει νόημα, επειδή η τιμή του A (4) εξαρτάται από την τιμή του A (3). Η επανάληψη συνεχίζεται ...

* Α (0)
* Α'1)
* Α2)
* Α (3)
* Α (4)
* main (String [] args)

Όταν καλείται A (0), έχουμε φτάσει σε μια βασική περίπτωση. Αυτό σημαίνει ότι η αναδρομή ολοκληρώθηκε και, αντί να κάνει μια αναδρομική κλήση, επιστρέφεται μια τιμή. Το A (0) βγαίνει από τη στοίβα και οι υπόλοιπες κλήσεις είναι στη συνέχεια σε θέση να βγουν από τη στοίβα διαδοχικά μέχρι ο A (4) να μπορέσει τελικά να επαναφέρει την αξία του στο κύριο.

Εδώ είναι η διαίσθηση: η τιμή επιστροφής οποιασδήποτε κλήσης μεθόδου εξαρτάται από την τιμή επιστροφής μιας άλλης κλήσης μεθόδου. Επομένως, όλες οι κλήσεις μεθόδου πρέπει να αποθηκεύονται στη μνήμη μέχρι να επιτευχθεί μια βασική περίπτωση. Όταν επιτευχθεί η βασική περίπτωση, οι τιμές αρχίζουν να καθορίζονται σαφώς, αντί να καθορίζονται αναδρομικά. Για παράδειγμα, το A (1) ορίζεται αναδρομικά μέχρι να ξέρει τον ορισμό της βασικής περίπτωσης, 1. Στη συνέχεια, είναι καλά καθορισμένος ως 2 φορές 1.

Όταν προσπαθούμε να λύσουμε προβλήματα με την επανάληψη, είναι συχνά πιο αποτελεσματικό να σκεφτούμε τη σειρά με την οποία επιστρέφονται οι αξίες. Αυτό είναι το αντίθετο από τη σειρά με την οποία πραγματοποιούνται κλήσεις. Αυτή η σειρά είναι πιο χρήσιμη επειδή αποτελείται από καλά καθορισμένες τιμές, αντί για επαναλαμβανόμενες τιμές.

Για αυτό το παράδειγμα, είναι πιο χρήσιμο να θεωρήσουμε ότι το Α (0) επιστρέφει 1, και στη συνέχεια το Α (1) επιστρέφει 2 φορές 1, και στη συνέχεια το Α (2) επιστρέφει 2 φορές Α (1) κ.ο.κ. Ωστόσο, όταν γράφουμε τον κώδικα μας, είναι πιο εύκολο να το πλαισιώσουμε με την αντίστροφη σειρά (η σειρά για την πραγματοποίηση των κλήσεων). Αυτός είναι ένας άλλος λόγος που θεωρώ χρήσιμο να γράψω τη βασική υπόθεση και την αναδρομική περίπτωση κάτω πριν γράψω οποιοδήποτε κώδικα.

Μέθοδοι Βοηθός και Επαναλήψεις vs. Loops

Είμαστε προγραμματιστές, όχι μαθηματικοί, έτσι η επανάληψη είναι απλά ένα εργαλείο. Στην πραγματικότητα, η επανάληψη είναι ένα σχετικά απλό εργαλείο. Είναι πολύ παρόμοιο με τους βρόχους στο ότι τόσο οι βρόχοι όσο και η επανάληψη προκαλούν επανάληψη στο πρόγραμμα.

Μπορεί να έχετε ακούσει ότι οποιαδήποτε επαναλαμβανόμενη εργασία μπορεί να γίνει είτε με βρόχο είτε με βρόχο. Ορισμένες εργασίες προσφέρονται καλύτερα, ενώ οι βρόχοι και άλλες εργασίες προσφέρονται καλύτερα για βρόχους.

Το ίδιο ισχύει και με αυτό το νέο εργαλείο, την υποτροπή. Οποιαδήποτε επαναλαμβανόμενη εργασία μπορεί να επιτευχθεί είτε με βρόχο είτε με επανάληψη, αλλά ορισμένες εργασίες προσφέρονται καλύτερα σε βρόχους και άλλοι προσφέρονται καλύτερα στην αναδρομή.

Όταν χρησιμοποιούμε βρόχους, είναι μερικές φορές απαραίτητο να χρησιμοποιήσουμε μια τοπική μεταβλητή για να «παρακολουθούμε» έναν υπολογισμό. Ακολουθεί ένα παράδειγμα.

δημόσιο διπλό ποσό (διπλό [] α)
{
    διπλό άθροισμα = 0,0;
    για το (int i = 0, i 
}}

Αυτή η μέθοδος παίρνει μια σειρά διπλών ως παράμετρο και επιστρέφει το άθροισμα αυτού του πίνακα. Χρησιμοποιεί μια τοπική μεταβλητή, άθροισμα, για να παρακολουθεί το λειτουργικό άθροισμα. Όταν ολοκληρωθεί ο βρόχος, το άθροισμα θα κρατήσει το πραγματικό άθροισμα όλων των τιμών στη συστοιχία και αυτή η τιμή επιστρέφεται. Αυτή η μέθοδος έχει στην πραγματικότητα δύο άλλες τοπικές μεταβλητές που είναι λιγότερο προφανείς. Υπάρχει ο διπλός πίνακας α, του οποίου το πεδίο είναι η μέθοδος, και ο iterator i (παρακολουθεί τον δείκτη), του οποίου το πεδίο εφαρμογής είναι ο βρόχος.

Τι θα συμβεί αν θέλαμε να πετύχουμε το ίδιο έργο χρησιμοποιώντας την επανάληψη;

δημόσιο διπλό recursiveSum (διπλό [] a)
    # υπολογίζει αναδρομικά το ποσό

Αυτό το καθήκον είναι επαναλαμβανόμενο, επομένως είναι δυνατό να το κάνουμε χρησιμοποιώντας υποτροπή, αν και είναι πιθανότατα πιο κομψά επιτυχής χρησιμοποιώντας ένα βρόχο. Απλά πρέπει να δημιουργήσουμε μερικές τοπικές μεταβλητές για να παρακολουθήσουμε το άθροισμα εργασίας και τον δείκτη, σωστά;

Αλίμονο, αυτό είναι αδύνατο. Οι τοπικές μεταβλητές υπάρχουν μόνο στο πλαίσιο μιας κλήσης μεμονωμένης μεθόδου και η επανάληψη χρησιμοποιεί επαναλαμβανόμενες κλήσεις μεθόδου για την πραγματοποίηση μιας επαναλαμβανόμενης εργασίας. Αυτό σημαίνει ότι οι τοπικές μεταβλητές είναι σχεδόν άχρηστες όταν χρησιμοποιούμε υποτροπή. Εάν γράφετε μια αναδρομική μέθοδο και αισθάνεστε σαν να χρειάζεστε μια τοπική μεταβλητή, ίσως χρειαστείτε μια μέθοδο βοηθού.

Μια μέθοδος helper είναι μια αναδρομική μέθοδος που χρησιμοποιεί πρόσθετες παραμέτρους για να παρακολουθεί τις τιμές. Για το recursiveSum, η μέθοδος βοήθειας μπορεί να φαίνεται ως εξής:

δημόσιο διπλό recursiveSum (διπλό [] a, διπλό άθροισμα, δείκτης int)
{
    αν (index == a.length)
        ποσό επιστροφής ·
    άθροισμα + = ένα [δείκτης].
    επιστροφή recursiveSum (α, άθροισμα, ευρετήριο + 1);
}}

Αυτή η μέθοδος δημιουργεί το άθροισμα μεταφέροντας την τιμή εργασίας σε μια νέα κλήση μεθόδου με τον επόμενο ευρετήριο. Όταν δεν υπάρχουν περισσότερες τιμές στη συστοιχία, το άθροισμα εργασίας είναι το πραγματικό άθροισμα.

Τώρα έχουμε δύο μεθόδους. Η "μέθοδος εκκίνησης" και η μέθοδος βοήθειας.

δημόσιο διπλό recursiveSum (διπλό [] a)
    # υπολογίζει αναδρομικά το ποσό
δημόσιο διπλό recursiveSum (διπλό [] a, διπλό άθροισμα, δείκτης int)
{
    αν (index == a.length)
        ποσό επιστροφής ·
    άθροισμα + = ένα [δείκτης].
    επιστροφή recursiveSum (α, άθροισμα, ευρετήριο + 1);
}}

Ο όρος "βοηθητική μέθοδος" είναι στην πραγματικότητα ένα κομμάτι μιας κακής κατάδειξης. Αποδεικνύεται ότι η μέθοδος βοηθού κάνει όλη τη δουλειά και η άλλη μέθοδος είναι απλώς ένας εκκινητής. Απλώς καλεί τη μέθοδο helper με τις αρχικές τιμές που ξεκινούν την επανάληψη.

δημόσιο διπλό recursiveSum (διπλό [] a)
    επιστρέφει recursiveSum (a, 0.0, 0);
δημόσιο διπλό recursiveSum (διπλό [] a, διπλό άθροισμα, δείκτης int)
{
    αν (index == a.length)
        ποσό επιστροφής ·
    άθροισμα + = ένα [δείκτης].
    επιστροφή recursiveSum (α, άθροισμα, ευρετήριο + 1);
}}

Σημειώστε ότι οι τιμές που χρησιμοποιούνται στην κλήση εκκίνησης στη μέθοδο helper είναι οι ίδιες τιμές που χρησιμοποιούνται για την αρχικοποίηση των τοπικών μεταβλητών στο παράδειγμα βρόχου. Αρχικοποιούμε τη μεταβλητή που χρησιμοποιείται για να παρακολουθήσουμε το άθροισμα στο 0,0 και αρχικοποιούμε την μεταβλητή που χρησιμοποιείται για να παρακολουθείτε τον δείκτη στο 0.

Νωρίτερα, είπα ότι οι τοπικές μεταβλητές είναι άχρηστες στο πλαίσιο της υποτροπής. Αυτό δεν είναι εντελώς αληθές, επειδή οι παράμετροι της μεθόδου είναι πράγματι τοπικές μεταβλητές. Δουλεύουν για την επανάληψη επειδή δημιουργούνται νέες κάθε φορά που ονομάζεται η μέθοδος. Όταν εκτελείται η επανάληψη, υπάρχουν πολλές κλήσεις μεθόδων που αποθηκεύονται στη στοίβα κλήσεων και ως αποτέλεσμα υπάρχουν πολλά αντίγραφα των τοπικών μεταβλητών.

Μπορείτε να ρωτήσετε, "Αν η μέθοδος βοηθού κάνει όλη τη δουλειά, γιατί χρειαζόμαστε ακόμη και τη μέθοδο εκκίνησης; Γιατί δεν ονομάζουμε απλά τη μέθοδο helper με τις αρχικές τιμές και τότε χρειάζεται μόνο να γράψουμε μία μέθοδο; "

Λοιπόν, θυμηθείτε ότι προσπαθούσαμε να αντικαταστήσουμε τη μέθοδο που χρησιμοποιούσαμε για βρόχο. Αυτή η μέθοδος ήταν απλή. Έλαβε μια παράμετρο ως παράμετρο και επέστρεψε το άθροισμα του πίνακα ως διπλό. Αν αντικαταστήσαμε αυτή τη μέθοδο με μία που πήρε τρεις παραμέτρους, θα πρέπει να θυμόμαστε να την ονομάζουμε με τις σωστές τιμές εκκίνησης. Εάν κάποιος άλλος ήθελε να χρησιμοποιήσει τη μέθοδο σας, θα ήταν αδύνατο αν δεν ήξερε τις αρχικές τιμές.

Για αυτούς τους λόγους, είναι λογικό να προσθέσουμε μια άλλη μέθοδο που να φροντίζει για αυτές τις αρχικές τιμές για εμάς.

Τυλίγοντας

Η επανάληψη είναι μια αρκετά προκλητική ιδέα, αλλά το κάνατε μέχρι το τέλος της εξήγησής μου. Ελπίζω να καταλάβετε τη μαγεία λίγο καλύτερα. Τώρα σας δίνω επίσημα τον τίτλο του «Grand-Wizard of Recursion». Συγχαρητήρια!