Τετάρτη, 30 Δεκεμβρίου 2015

Τμήματα ή Ρουτίνες. Η διαφορά στη Μ2000

Ανέβηκε και η αναθεώρηση 132. (διορθώνει μερικά bug μόνο, που δεν θα αναφερθούν εδώ)

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

Οι τρεις εκδόσεις του πιάνου (η 2η έκδοση παρουσιάστηκε και αντικαταστάθηκε με την 3η), παραμένουν σε αναρτήσεις εδώ για να παρατηρήσουμε πως ένα πρόγραμμα μεγαλώνει, με την ευκολία των ρουτινών.

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

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

Ρουτίνα Όνομα1()

Τέλος Ρουτίνας
Η κλήση της ρουτίνας γίνεται με τη Διαμέσου (GOSUB). Επειδή οι ρουτίνες αναζητούνται από το τέλος, τις βάζουμε στο τέλος.

Μάλιστα αν η ροή προγράμματος βρει την λέξη Ρουτίνα τότε θεωρεί ότι έφτασε στο τέλος και τερματίζει το τμήμα!

Που μπαίνουν οι Ρουτίνες;
Οι ρουτίνες δεν βρίσκονται "μόνες" τους κάπου αλλά σε τμήματα. Τμήματα μπορούν να έχουν άλλα τμήματα και ρουτίνες. Η διαφορά βρίσκεται στο ότι οι ρουτίνες βλέπουν ότι έχει το τμήμα που τις καλεί, ενώ το τμήμα στο τμήμα δεν έχει πρόσβαση στο "πατρικό". Τα κοινά που έχουν τμήματα και ρουτίνες είναι ότι βρίσκονται σε τμήματα και έχουν ίδιο σωρό τιμών (ειδική στοίβα της Μ2000). Άρα μπορούμε να καλέσουμε μια ρουτίνα και να πάρουμε (Εφόσον το θέλουμε) μια επιστροφή στον σωρό τιμών (μπορεί στη κορυφή να έχουμε έναν αριθμό που να πληροφορεί πόσα στοιχεία ακολουθούν ή να έχουμε ένα πίνακα, ο σωρός δέχεται και ανίγραφο πίνακα σε ένα στοιχείο).

Οι ρουτίνες είναι "ελαφριές" δομές κώδικα
Αν δει κανείς τον ορισμό της ρουτίνας παραπάνω θα προσέξει ότι δεν έχει μπλοκ.εντολών {} Αυτό σημαίνει ότι χρειάζεται λιγότερους πόρους για να τρέξει (τα μπλοκ είναι μια στρώση ανώνυμου κώδικα και μπορούν να γίνουν δομή επανάληψης). Τα μπλοκ είναι εσωτερικά κλήσεις σε συνάρτηση και τρώνε πόρους από τον Stack του περιβάλλοντος (ο οποίος είναι περιορισμένος, γιατί είναι σε μια μονοκόματη περιοχή). Οι κλήσεις στις ρουτίνες με την ΔΙΑΜΕΣΟΥ γίνονται με στοίβα επιστροφής ανά τμήμα, μια δυναμική λίστα δηλαδή που κάθε στοιχείο της έχει ξεχωριστό χώρο (δεν γράφονται διαδοχικά όπως στο Stack). Προφανώς το Stack είναι πιο γρήγορο για τα τμήματα, αλλά επειδή οι ρουτίνες δεν αλλάζουν το αντικείμενο επεξεργασίας (process object), όπως γίνεται σε κάθε κλήση τμήματος, αλλά συνεχίζουν στο ίδιο αντικείμενο, έχουμε πάλι γρήγορη διαδοχή.

Που βρίσκεται η Ρουτίνα και που το Τμήμα
Μια σημαντική διαφορά ρουτινών και τμημάτων είναι ότι τα τμήματα έχουν τρεις θέσεις σε σχέση με τις ρουτίνες που έχουν δυο: Την θέση στο κώδικα, την θέση που λαμβάνουν στη λίστα τμημάτων (και για το λόγο αυτό ο ορισμός τους πρέπει να περάσει από τη ροή προγράμματος για να μπει στη λίστα), και τέλος το εκτελέσιμο αντίγραφο το οποίο καταναλώνεται σε ένα αντικείμενο επεξεργασίας. Μια ρουτίνα θα κληθεί μόνο μέσα από ένα τμήμα (ή συνάρτηση, και οι συναρτήσεις είναι σαν τα τμήματα, έχουν αντικείμενο επεξεργασίας), με αναζήτηση όχι στο εκτελέσιμο αντίγραφο (αφού αυτό καταναλώνεται...θα εξηγήσω στο τέλος τι σημαίνει κατανάλωση..κώδικα), αλλά στη δεύτερη θέση, στη λίστα τμημάτων. Αν δεν βρεθεί εκεί τότε ο διερμηνευτής πάει στη πρώτη θέση, δηλαδή στη λίστα τμημάτων εγγράφεται η πηγή απ΄όπου φορτώθηκε το τμήμα (αν έγινε η φόρτωση από κώδικα και όχι από το δίσκο). Ο λόγος που γίνεται αυτή η δεύτερη αναζήτηση είναι για λόγους οικονομίας, επειδή διαφορετικά μια ρουτίνα δεν θα μπορούσε να χρησιμοποιηθεί από "αδελφό" τμήμα (στο ίδιο πατρικό) και θα έπρεπε να γραφτεί ξανά!  Ουσιαστικά η ρουτίνα υπάρχει στο κώδικα και έρχεται όποτε θέλουμε με την Διαμέσου στο αντικείμενο εκτέλεσης (άρα σε δυο μέρη)

Σχετικά με το παράδειγμα
Αν το παρακάτω τμήμα λέγεται Α τότε θα φτιάξει την Α.Χ, το τμήμα Α.Κ και θα πάει η ροή στην ρουτίνα όπου το Χ είναι το Α.Χ (επειδή το Α το γνωρίζει το αντικείμενο επεξεργασίας), μετά θα κληθεί η Κ, και εδώ με το τρόπο της κάλεσε όπου το όνομα Κ δεν θα χρησιμοποιηθεί αλλά το Α[1] και θα φτιαχτεί η Α[1].Χ και όταν πάει η ροή στη ρουτίνα το αντικείμενο επεξεργασίας στο Α[1] θα βρει την Κ ως Α[1].Κ. Μόλις το Κ τερματίσει το αντικείμενο επεξεργασιας θα χαθεί, θα χαθούν και οι μεταβλητές του.
Ουσιαστικά η Διαμέσου αντιγράφει το κώδικα από τη Ρουτίνα μέχρι το τέλος (δεν ψάχνει καν για το Τέλος Ρουτίνας) στο αντικείμενο επεξεργασίας και όταν βρεθεί το Τέλος Ρουτίνας, ή το Έξοδος Ρουτίνας ή ακόμα και το Επιστροφή, τότε παίρνει εντολή το αντικείμενο επεξεργασίας να πετάξει τον κώδικα και να επιστρέψει στο κώδικα που ήταν πριν, ακόμα και αν ο κώδικας δεν ήταν από το τμήμα αλλά από άλλη ρουτίνα (όπως στο δεύτερο πρόγραμμα), αλλά από τον ίδιο πηγαίο κώδικα!
Στο τρίτο πρόγραμμα η ρουτίνα α() δεν βρίσκει τη β() μέσα στο τμήμα Κ, ενώ στο τμήμα ΛΛ δουλεύει!
Σημασία όμως έχει να κατανοήσει κανείς πως η θέση της ρουτίνας έχει σχέση με την οικονομία στο κώδικα (να μην έχουμε πολλά αντίγραφα) και όχι με κλήση έξω από το τμήμα.
Όλες οι διαμέσου γίνονται στο ίδιο αντικείμενο εκτέλεσης (και θέλουν ελάχιστους πόρους).

\\ 1ο Πρόγραμμα
Χ=4
Τμήμα Κ {
      Χ=10
      Διαμέσου α()
}
Διαμέσου α() \\ Οκ  4
Κάλεσε Κ \\ Οκ 10
Ρουτίνα α()
      Τύπωσε "Οκ", Χ
Τελος Ρουτίνας


\\ 2ο Πρόγραμμα
Χ=4
Τμήμα Κ {
      Χ=10
      Διαμέσου α()
}
Διαμέσου α() \\ Οκ  4
Κάλεσε Κ \\ Οκ 10
Ρουτίνα β()
      Τύπωσε "Οκ2"
Τέλος Ρουτίνας
Ρουτίνα α()
      Διαμέσου β()
      Τύπωσε "Οκ", Χ
Τελος Ρουτίνας

\\ 3ο Πρόγραμμα
Τμήμα ΛΛ {
Χ=4
Τμήμα Κ {
      Χ=10
      Διαμέσου α()
}
Διαμέσου α() \\ Οκ  4
\\ Κάλεσε Κ      \\ θα βγει λάθος!
Ρουτίνα α()
      Διαμέσου β()
      Τύπωσε "Οκ", Χ
Τελος Ρουτίνας
}
Κάλεσε ΛΛ
Ρουτίνα β()
      Τύπωσε "Οκ2"
Τέλος Ρουτίνας


Δείτε και κάτι άλλο. Το παρακάτω δουλεύει γιατί η αναζήτηση της ρουτίνας γίνεται στη κλήση, και ψάχνει στο κώδικα το Ρουτίνα ή Ρουτίνα στη πρώτη γραμμή και μετά κοιτάει αν υπάρχει το α(, εδώ το βρίσκει παρόλο που είναι στο α$ ως σταθερά αλφαριθμητική και όχι ως πρόγραμμα. Στην ουσία το πρόγραμμα είναι μια σταθερά αλφαριθμητική.

Τμήμα ΛΛ {
α$={
Ρουτίνα α()
      Διαμέσου β()
      Τύπωσε "Οκ",  Χ
Τελος Ρουτίνας
}
Χ=4
Διαμέσου α() \\ Οκ  4
}
Κάλεσε ΛΛ
Ρουτίνα β()
      Τύπωσε "Οκ2"
Τέλος Ρουτίνας


Όταν τρέχει ένα πρόγραμμα τότε το αντικείμενο επεξεργασίας το καταναλώνει, δηλαδή ότι εκτελείται αφαιρείται! Μια επανάληψη ενός μπλοκ γίνεται επειδή έχει κρατηθεί αντίγραφο του κώδικα για επανάληψη! Μια ΠΡΟΣ όπως και η ΔΙΑΜΕΣΟΥ κάνουν έξοδο από το μπλοκ και νέα είσοδο εκεί που θέλουμε, με ανανεωση του κώδικα προς κατανάλωση!

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

Τμήμα ΔεςΕδω {
      α=1000
      γενική Κ$
      Κ$<=Τμήμα$
      ΑΒ=10
      Τμήμα ΑΛΦΑ1
      ΑΒ=20
      Τύπωσε ΑΒ \\20
      Διαμέσου Πολ(10)
      Τμήμα Κ$
      Τύπωσε ΑΒ \\10
      Διαμέσου Πολ(10)
      Τμήμα ΑΛΦΑ1
      Τύπωσε ΑΒ \\20
      Διαμέσου Πολ(10)
      Τμήμα Κ$ \\ αν δεν το βάλω η α θα είναι άγνωστη
      Τύπωσε α
}
Κάλεσε ΔεςΕδω
Ρουτίνα Πολ(α)
      Τύπωσε ΑΒ*α
Τέλος Ρουτίνας


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

Το σύμβολο & σημαίνει πέρασμα με αναφορά. Όταν καλούμε το ΛΜ δεν διαβάζουμε από το σωρό την παράμετρο, το αφήνουμε να την διαβάσει το Λ.Μ δηλαδή το τμήμα Μ στην ομάδα Λ.

Η ομάδα α (το αντίγραφό της) που γράφτηκε στο αα(1) δεν είναι πια ένα στατικό αντικείμενο! Την περνάμε σε δυο τμήματα μέσω του πίνακα, στο ένα με αναφορά και στο άλλο με αντιγραφή. Στην αντιγραφή πίνακα έχουμε αντιγραφή και της ομάδας (δεν κρατάει δηλαδή αναφορά στην ομάδα το στοιχείο πίνακα αλλά περιέχει μια ομάδα με ότι έχει αυτή, τμήματα, συναρτήσεις, άλλους πίνακες, ομάδες). Τα μη στατικά αντικείμενα (ή πτητικά) είναι σαν προγράμματα χωρίς όνομα!


Ομάδα α {
      ι=5
      Τμήμα Κ {
            Διάβασε Χ
            Διαμέσου α()
            Τύπωσε Χ*
            ++
       
      }
      Ομάδα Λ {
            ι=10
            Τμήμα Μ {
                  Διάβασε Χ
                  Διαμέσου α()
                  Τύπωσε Χ*
                  ++
            }
       
      }
      Τμήμα ΛΜ {
            .Λ.Μ
            Ρουτίνα α()
                  Τύπωσε \\ τυπώνει το ι από κάθε ομάδα
            Τέλος Ρουτίνας
      }
}
Πίνακας ΚΚ(10)
ΚΚ(1)=α \\ αντίγραφο
Α.Κ 100 \5000
Α.Λ.Μ 100 \ 1000
Α.Κ 100 \600
Α.Λ.Μ 100 \1100
Α.ΛΜ 100 \1200


ΚΚ(1).ΛΜ 5000 \ 50000
Για ΚΚ(1) {
      .ΛΜ 5000 \ 55000
}
ΚΚ(1).ΛΜ 5000 \ 60000



Τμήμα ΔεςΚαιΕδώ {
      Διάβασε &αα()
      αα(1).ΛΜ 5000
}
ΔεςΚαιΕδώ &ΚΚ()
Τμήμα ΔεςΚαιΕδώ2 {
  Διάβασε αα()
  αα(1).ΛΜ 4000
}
ΔεςΚαιΕδώ2 ΚΚ()


Δείτε επίσης και τις παλιές αναρτήσεις πιο συνοπτικές για την Ρουτίνες:
Χρήση της ΔΙΑΜΕΣΟΥ ή GOSUB στη Μ2000 μέρος Α
και εδώ για την απλή χρήση της Διαμέσου με χρήση ετικετών και όχι ρουτινών
Χρήση της ΔΙΑΜΕΣΟΥ ή GOSUB στη Μ2000 μέρος Β