Κυριακή, 29 Νοεμβρίου 2015

Αναδρομική κλήση στη Μ2000

(Προχωρημένο θέμα, και σχετίζεται και με την αναθεώρηση 98)

Στη Μ2000 αναδρομική κλήση υπάρχει σε δυο τύπους. Ο ένας χρησιμοποιεί την στοίβα (stack) που αποδίδει το σύστημα στο περιβάλλον (και δυστυχώς δεν μπορεί να επεκταθεί), με αποτέλεσμα να μπορούν να γίνουν μέχρι 128 κλήσεις (κάθε φορά μια συνάρτηση καλεί τον εαυτό της με μια παραλλαγή στις παραμέτρους). Ο άλλος τύπος χρησιμοποιεί την ρουτίνα. Αυτός ο τύπος δεν έχει περιορισμούς εκτός από ένα όριο που εμείς βάζουμε ως ΟΡΙΟ.ΑΝΑΔΡΟΜΗΣ. Εξ ορισμού είναι οι 1000 κλήσεις. Και στις δυο περιπτώσεις δεν μετράει τι φτιάχνουμε μέσα στις συναρτήσεις και τις ρουτίνες (γιατί αποδίδεται μνήμη εκτός στοίβας, δηλαδή μπορούμε να χρησιμοποιήσουμε όλη την πραγματική και την εικονική μνήμη μέχρι να φτάσουμε στα όρια λάθους)

Στην νέα αναθεώρηση 98 ασχολήθηκα με τα όρια των αναδρομικών κλήσεων.και έθεσα το όριο 128 κλήσεων στις συναρτήσεις.
ΔΕΝ ΙΣΧΥΕΙ ΠΙΑ ΑΥΤΟ ΤΟ ΟΡΙΟ ΑΛΛΑ ΤΟ ΝΕΟ: 14800 3375
το όριο κατέβηκε για να μπορεί να λειτουργεί η εντολή Αναλόγιο που δείχνει σελίδες Html. Το γιατί ο χώρος του stack εμποδίζει την λειτουργία του WebControl δεν είναι γνωστό. Από την 8.2 έκδοση, αναθεώρηση 11, κατέβηκε το όριο στο 3375.

Μπορούμε και σε ρουτίνες να κάνουμε ότι κάνουμε και με τις συναρτήσεις χωρίς το περιορισμό των 128 14800 3375 κλήσεων.

Εδώ σε μια μεταβλητή Όριο θέτουμε το νούμερο 3000 για να κάνουμε 3000 αναδρομικές κλήσεις! Οι ρουτίνες βλέπουν όλες τις μεταβλητές του τμήματος, αλλά μπορούν να κάνουν τοπικές μεταβλητές και πίνακες.Κάνουμε 3000 κλήσεις και φτιάχνουμε 3000Χ6 μεταβλητές (μαζί με τις ι και κ) και 3000 πίνακες των 30 στοιχείων. Η κ περνάει με αναφορά αλλά δημιουργείται νέο όνομα, απλά 3000 κ θα δείχνουν στην ίδια τιμή, την τιμή του Α.
Το &Α συμβολίζει το πέρασμα με αναφορά. Το &κ θα γίνει η μεταβλητή που θα πάρει την αναφορά του Α. Δεν μπορούμε να αποδώσουμε νέα αναφορά σε μια μεταβλητή που υπάρχει, μόνο σε καινούργια. Όμως η ρουτίνα στο όρισμά της κάνει τοπικές μεταβλητές. Στην κυριολεξία οι 18000 μεταβλητές ανήκουν στο τμήμα. Κάθε φορά όμως που θα επιστρέφουμε από μια ρουτίνα σε αυτήν που την κάλεσε, θα διαγράφεται μια σειρά μεταβλητών και ένας πίνακας. Στο τέλος θα υπάρχει μόνο η τιμή που μαζέψαμε στην Α.
Χρησιμοποιώ μια εντολή και μια μεταβλητή μόνο ανάγνωσης, για να πάρω το χρόνο εκτέλεσης. Ο φόρτος είναι ο χρόνος εκτέλεσης. Ο οποίος όμως εξαρτάται και από τη διαδικασία και από ότι τρέχει παράλληλα.
Έχω βάλει να τυπώνει ξεκινώντας από το 1. Στην επόμενη κλήση, το ι θα είναι 2999 και ο τύπος θα δώσει το 2.
Αν θέλουμε να δούμε το σφάλμα τότε βάζουμε στην εντολή ΟΡΙΟ.ΑΝΑΔΡΟΜΗΣ το ΟΡΙΟ-1. Θα βγεί λάθος στην τελευταία κλήση, γιατί βγήκαμε εκτός ορίων. Στο λάθος σβήνει όλες τις μεταβλητές και τυχόν τμήματα ή συναρτήσεις που έχουν δημιουργηθεί.

\\ Με όριο 10000 δουλεύει εξίσου καλά! (48Mbyte έπιασε κορυφή στη χρήση μνήμης, με το που τελειώνει γυρνάει στα 6Mbyte)

\\ Αναδρομή με ρουτίνα
ΟΡΙΟ=3000
ΟΡΙΟ.ΑΝΑΔΡΟΜΗΣ ΟΡΙΟ
Α=0
Αναλυτής
Διαμέσου Έλεγχος(&Α, ΟΡΙΟ)
Τύπωσε Α
Τύπωσε Φόρτος


Ρουτίνα Έλεγχος(&κ,ι)
      \\ φτιάχνει κάθε φορά μια τοπική σειρά μετ. και πίνακα
      Τοπικές α=ι,β,γ,δ, α(30)
      Αν ι<1 Τότε Έξοδος Ρουτίνας
      Τύπωσε ΟΡΙΟ-ι+1
      Διαμέσου Έλεγχος(&κ,α-1)
      κ+=ι
Τέλος Ρουτίνας


Διόρθωση: Είχα χρησιμοποιήσει το Έξοδος Ρουτίνας ως τελευταία εντολή. Δεν ήταν λάθος αφού λειτουργεί και έτσι, αλλά πιο ωραίο φαίνεται το Τέλος Ρουτίνας
Μπορούμε να μην χρησιμοποιήσουμε πέρασμα με αναφορά αλλά την Α (την κάνω Α1 για να μην είναι ίδια με την τοπική α)

\\ Αναδρομή με ρουτίνα
ΟΡΙΟ=3000
ΟΡΙΟ.ΑΝΑΔΡΟΜΗΣ ΟΡΙΟ
Α1=0
Αναλυτής
Διαμέσου Έλεγχος(ΟΡΙΟ)
Τύπωσε Α1
Τύπωσε Φόρτος


Ρουτίνα Έλεγχος(ι)
      \\ φτιάχνει κάθε φορά μια τοπική σειρά μετ. και πίνακα
      Τοπικές α=ι,β,γ,δ, α(30)
      Αν ι<1 Τότε Έξοδος Ρουτίνας
      Τύπωσε ΟΡΙΟ-ι+1
      Διαμέσου Έλεγχος(α-1)
      Α1+=ι

Τέλος Ρουτίνας


Ας υποθέσουμε ότι το παραπάνω τμήμα είναι γραμμένο ως β. Αλλάζουμε την ΟΡΙΟ=3000 με την παρακάτω:

Διάβασε ΟΡΙΟ
και επιπλέον σκιάζουμε την  Τύπωσε ΟΡΙΟ-ι+1
\\      Τύπωσε ΟΡΙΟ-ι+1

Τώρα φτιάχνουμε ένα άλλο τμήμα, έστω γ  (με Σ γ enter  φτιάχνουμε το τμήμα).
β 1000
β 2000
β 3000
Έχουμε τρεις εντολές που είναι το τμήμα με διαφορετικό όριο.
Περιμένουμε το β 2000 να έχει το διπλάσιο χρόνο εκτέλεσης από το πρώτο με το όριο 1000;
Δεν συμβαίνει αυτό. Αν ζητάμε περισσότερη μνήμη τότε πάμε σε πιο αργές διαδικασίες. 

Σε έναν υπολογιστή πήρα τις παρακάτω τιμές:

αποτέλεσμα 500500
χρόνος 2087.78

αποτέλεσμα 2001000  
χρόνος 9229.57

αποτέλεσμα 4501500
χρόνος 21196.714

(οι χρόνοι είναι σε χιλιοστά του δευτερολέπτου. Από το  QueryPerformanceFrequency function τωνWindows λαμβάνουμε το CountsPerSecond (Θα τους λέγαμε και κύκλους ανά δευτερόλεπτο, άρα είναι συχνότητα). Και μετά λαμβάνουμε μετρήσεις από το  QueryPerformanceCounter function (αυτό μας δίνει τα ΜΑΡΚ1 και ΜΑΡΚ2)
Οπότε ο τύπος είναι για χιλιοστά του δευτερολέπτου:((MARK2 - MARK1) / CountsPerSecond) * 1000. Αυτό μας δίνει ο Φόρτος. Εδώ αν τρέχουν άλλες εφαρμογές θα αυξηθεί ο χρόνος εκτέλεσης, έτσι αν γνωρίζουμε το χρόνο εκτέλεσης όταν δεν τρέχει τίποτα άλλο, τότε η οποιαδήποτε αύξηση θα είναι ανάλογη του φορτίου της CPU, της μονάδας επεξεργασίας.


Και τα τμήματα μπορούν να κληθούν με αναδρομή αλλά με την εντολή Κάλεσε. Ισχύει και εδώ το όριο των 128 14800 3375 κλήσεων (θα σταματήσει στις 128  14800 3375 κλήσεις)

Τμήμα Έλεγχος {
      Διάβασε ι
      Τοπικες α,β,γ,δ, α(30)
      Αν ι<1 Τότε Έξοδος
      Τύπωσε 3001-ι
      Κάλεσε Έλεγχος ι-1
}
Αναλυτής
Κάλεσε Έλεγχος 3000
Τύπωσε Φόρτος

Εδώ είναι η συνάρτηση, όπου και πάλι υπάρχει το όριο των 128  14800 3375 κλήσεων. Η διαφορά με τη κλήση σε τμήμα είναι ότι η συνάρτηση φτιάχνει νέο σωρό τιμών (ειδική στοίβα της Μ2000). Έτσι ότι επιστρέφει είναι από το ίσον και ότι τυχόν περνάμε με αναφορά. Το τμήμα όμως καλεί τον εαυτό του διατηρώντας την πρόσβαση στο σωρό για να πάει και να αφήσει τιμές..Υπάρχει μεγαλύτερη ελευθερία (άρα και πιο εύκολο μπέρδεμα ή λάθος...και για το λόγο αυτό η αναδρομική κλήση στα τμήματα γίνεται με πρόσθετη εντολή, μην γίνει λάθος από εκείνους που ξεκινούν με την Μ2000).
Από το σωρό τιμών πάντα με τη διάβασε σηκώνουμε στοιχεία (δηλαδή δεν γίνεται απλά αντιγραφή σε μια μεταβλητή αλλά διαγράφεται από την κορυφή του και η κορυφή του δείχνει το επόμενο στοιχείο). Όταν περνάμε τιμή με αναφορά, περνάμε την αναφορά μόνο, στο σωρό τιμών και η διάβασε βρίσκει από την αναφορά το τι θα κάνει. Ο σωρός τιμών χρησιμοποιείται κάθε φορά που καλούμε συνάρτηση, ή τμήμα ή ρουτίνα. Στη ρουτίνα ότι υπάρχει στις παρενθέσεις πάνε σε μια Διάβασε που δεν βλέπουμε. Να θυμίσω εδώ ότι η Διαμέσου καλεί την ρουτίνα, και αυτή πρέπει να υπάρχει στο τμήμα που υπάρχει και η διαμέσου, στο τέλος του. Η αναζήτηση γίνεται από το τέλος.


Συνάρτηση Έλεγχος {
      Διάβασε ι
      Τοπικες α,β,γ,δ, α(30)
      Αν ι<1 Τότε Έξοδος
      Τύπωσε 3001-ι
      =Έλεγχος(ι-1)
}
Αναλυτής
Α=Έλεγχος(3000)
Τύπωσε Φόρτος


Σε ένα τμήμα μπορούμε να έχουμε άλλα τμήματα, συναρτήσεις και ρουτίνες, καθώς και νήματα. Επίσης στις συναρτήσεις ανήκουν και οι κλάσεις, οι οποίες επιστρέφουν ομάδες (είναι τα αντικείμενα της Μ2000). Εδώ έχουμε μια ομάδα άλφα που έχει μια φωλιασμένη ομάδα βήτα. Η ομάδα βήτα έχει την συνάρτηση Παραγοντικό(). Εμείς καλούμε μια συνάρτηση από την άλφα η οποία καλεί την συνάρτηση της εσωτερικής βήτα!
Η γραμμή αυτή: Αν π<2 Τότε { =1 } Αλλιώς { =Παραγοντικό(π-1)*π }
μπορεί να γραφτεί και χωρίς την τελεία πριν το Παραγοντικό διότι ο διερμηνευτής το βρίσκει το όνομα (ενώ με τελεία έχουμε αναφορά σε ότι άλλο είναι στο ίδιο επίπεδο, π.χ. τα γ  και δ θα τα διαβάζαμε στην συνάρτηση Παραγοντικό ως .γ και .δ
Στις συναρτήσεις ενός τμήματος δεν μπορούμε να διαβάζουμε τις μεταβλητές του τμήματος. Ομοίως ένα τμήμα ενός τμήματος δεν έχει πρόσβαση στις μεταβλητές του πατρικού. Οι συναρτήσεις και τα τμήματα δημιουργούν το δικό τους χώρο ονόματος (namespace). Οι ομάδες εδώ είναι γειωμένες, δηλαδή έχουν όνομα και αυτό σημαίνει ότι δεν μπορούν να μετακινηθούν. Μπορούμε όμως να βάλουμε ένα αντίγραφο σε μια πτητική ομάδα! 

\ Αναδρομή σε κλήση συνάρτησης σε ομάδα
Ομάδα αλφα {
      Ομάδα βητα {
            γ=2
            δ=3
            Συνάρτηση Παραγοντικό {
            Διάβασε π
            Αν π<2 Τότε { =1 } Αλλιώς { =.Παραγοντικό(π-1)*π }
            }
       }
      γ=100
      Πίνακας α(20)=3
      Πίνακας κ(5)=100
      Συνάρτηση κάποια {
            =*100
      }
      Συνάρτηση βγάλε_παραγοντικό {
             Διάβασε α
            =.βήτα.παραγοντικό(α)
      }
}
Τύπωσε αλφα.βγάλε_παραγοντικό(10)


Πίνακας Α(100)
Α(3)=άλφα
Τύπωσε Α(3).βγάλε_παραγοντικό(10)

Τώρα βάλαμε στο Α(3) μια πτητική ομάδα, ένα αντίγραφο της άλφα.
Αν θέλουμε με μια εντολή όπως αυτή:
Βάλε Α()
βάζω στη κορυφή του σωρού ένα αντίγραφο από τον πίνακα Α(), που θα έχει στην θέση 3 ένα αντίγραφο του Α(3). Αν τώρα τερματίσει το τμήμα ο σωρός θα έχει τον πίνακα, και ότι έχει αυτός σε ένα και μόνο στοιχείο. Αυτές τις εντολές μπορούμε να τις γράψουμε στο ίδιο τμήμα ή στο πατρικό ή στη γραμμή εντολών. Με τη διάβασε ο πίνακας φεύγει από το σωρό και πάει στον Κ() και στο Κ(3) παραμένει η ομάδα κρυμμένη! Μπορούμε να διαβάσουμε τις μεταβλητές αν ξέρουμε ποιες είναι!Το ίδιο και με τις συναρτήσεις. Εδώ μάλιστα καλούμε την βγάλε_παραγοντικό() ενώ αυτή καλεί την .βήτα.παραγοντικό() που είναι πιο μέσα, ανήκει στο .βήτα  (αυτό λέγεται ενθυλάκωση στη Μ2000).

Διάβασε Κ()
Τύπωσε Κ(3).βγάλε_παραγοντικό(10)

Έτσι λέμε ότι η ομάδα είναι πτητική διότι αυτό που την δημιούργησε δεν υπάρχει πια! Σε αντίθεση οι γειωμένες, στέρεες ομάδες, χάνονται μόλις χάνεται ο δημιουργός τους.
Ασφαλώς μέσα σε πίνακες στις ομάδες μπορούν να υπάρχουν άλλες ομάδες με άλλους πίνακες! Έτσι μπορούμε να φτιάχνουμε δομές χωρίς χρήση δεικτών σε πραγματική μνήμη αλλά σε πίνακες! Αυτό είναι μια από τις ιδέες της Μ2000 που δεν συναντάει κανείς αλλού!

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου