Πέμπτη 12 Μαΐου 2016

Παράδειγμα με δυο φόρμες, η δεύτερη Modal

Αναθεώρηση 14 (έκδοση 8.1)

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

Από το παράδειγμα εδώ: Παράδειγμα με δυο φόρμες, και γράψιμο και στις δυο ταυτόχρονα!
Εδώ η δεύτερη φόρμα ανοίγει ως modal και σταματάει η ροή του προγράμματος εκεί για να βάλουμε κάτι στο textbox edit2. Βάζουμε ένα νήμα να κάνει εστίαση στο edit2 της φόρμας form2 που ανοίγουμε ως Modal (δηλαδή όλα τα άλλα παράθυρα γίνονται ανενεργά από το ποντίκι μας, ούτε μπορούν να επιλεχθούν με το  tab-ctrl)


set slow
Declare form1 Form
\\ use standard size and title
Declare edit1 TextBox Form form1
Method edit1, "move", 1000,2000,6000,600
With edit1,"Vartext" as edit1.text$


Declare form2 Form
\\ use standard size and title
Declare edit2 TextBox Form form2
Method edit2, "move", 1000,2000,6000,600
With edit2,"Vartext" as edit2.text$


Method form1,"show"
xx=0
NoForm1=False
thread { xx++ } as b interval 200
Function form1.Unload {
      NoForm1=True
}


Function form1.Click {
      edit2.text$=edit1.text$
            after 10 {
            after 10 { Method edit2, "GetFocus" }
            Method form2,"show", 1
            }
}
Main.Task 100 {
      Print xx
      if NoForm1 Then Exit
}
Show
threads erase
Declare form2 Nothing
Declare form1 Nothing


Απλή έκδοση, έχουν αφαιρεθεί τα νήματα, το ένα που αλλάζει αριθμό ΧΧ, και το βασικό το Main.Task που έγινε Every. Το Every ή Κάθε, δεν είναι νήμα, αλλά κώδικας που παίζει σαν να είναι νήμα, η διαφορά είναι ότι όταν θα ανοίξει η φόρμα form2 επειδή ανοίγει ως modal, θα σταματήσει την ροή του προγράμματος στο τμήμα, η οποία βρίσκεται στο Every {}. Θα παίξει μόνο η after, και όσες φορές κληθούν συναρτήσεις για εξυπηρέτηση γεγονότων (εδώ δεν έχουμε κάποια για το form2).
Στη προηγούμενη εκδοχή, το Main.Task δεν σταματάει, και με το διπλό After έχουμε φύγει από την ρουτίνα εκτέλεσης του form1.Click, δηλαδή ουσιαστικά εκτελείται ως εκτός σειράς εντολή (αυτό έχει το νόημα της παράλληλης εκτέλεσης).

set slow
Declare form1 Form
\\ use standard size and title
Declare edit1 TextBox Form form1
Method edit1, "move", 1000,2000,6000,600
With edit1,"Vartext" as edit1.text$


Declare form2 Form
\\ use standard size and title
Declare edit2 TextBox Form form2
Method edit2, "move", 1000,2000,6000,600
With edit2,"Vartext" as edit2.text$


Method form1,"show"
NoForm1=False
Function form1.Unload {
      NoForm1=True
}
Function form1.Click {
      edit2.text$=edit1.text$
      after 10 { Method edit2, "GetFocus" }
      Method form2,"show", 1
}
Every 100 {
      if NoForm1 Then Exit
}
Show
threads erase
Declare form2 Nothing
Declare form1 Nothing


lor="#2020>
Declare form1 Nothing



Τετάρτη 11 Μαΐου 2016

Πώς δουλεύει ο διερμηνευτής της Μ2000 Μέρος Α.

Η τρέχουσα έκδοση της Μ2000 είναι η 11. Το κείμενο εδώ γράφτηκε πριν από αρκετά χρόνια (έκδοση 8). Ακόμα και η 11η έκδοση λειτουργεί με άμεση εκτέλεση κατά τη μετάφραση. Εσωτερικά έχει Hash Tables όπου οι αναζητήσεις γίνονται σε O(1), και η επιλογή της συνάρτησης (μεθόδου) που καλεί η λέξη γίνεται με χρήση δείκτη στη συνάρτηση, οπότε δεν έχουμε διπλή αναζήτηση, μια αν υπάρχει η λέξη και μια άλλη να βρούμε στις υπάρχουσες ποια συνάρτηση δίνει. Σε πολλές περιπτώσεις υπάρχει look ahead, όπως στις εκφράσεις. Έτσι αν θέλουμε αριθμητική έκφραση θα γίνει αναζήτηση για το είδος της έκφρασης μόνο συντακτικά (χωρίς να μας ενδιαφέρει αν έχουμε σωστά/γνωστά αναγνωριστικά) και αν είναι σωστό τότε θα προχωρήσει ο μεταφραστής σε εκτέλεση. Ο διορθωτής προγραμμάτων (ενσωματωμένος στο περιβάλλον) ελέγχει αν έχουμε κλειστά αλφαριθμητικά και κλειστές αγκύλες, την στιγμή που τον κλείνουμε (με Esc) και μας λέει το πρόβλημα και αν θέλουμε όντως να βγούμε ή να επιστρέψουμε. Πολλές δομές γράφονται και χωρίς αγκύλες.

Ο μεταφραστής της Μ2000 είναι μια συνάρτηση που καλεί άλλες συναρτήσεις, και κάποιες από αυτές καλούν ξανά τον μεταφραστή, μέχρι που να τελειώσει το πρόγραμμα. Υπάρχει ένας Task Manager, που μπορεί να βάλει νήματα (εκτελέσεις του μεταφραστή) σε αναμονή, και να διαχειριστεί τις εκτελέσεις τους. Αυτό γίνεται με έναν χρονιστή των Windows, που δίνει tick στον Task Manager για να αποφασίζει αν θα εκτελεστεί άλλο task και πότε! Τα νήματα δουλεύουν είτε το ένα μετά το άλλο ανά διάρκεια νέας εκκίνησης που έχει οριστεί για το καθένα, ή μπορούμε να επιλέξουμε την γρήγορη εναλλαγή εκτέλεσης νημάτων σε επίπεδο εντολής. Εκτός από νήματα μπορούμε να διαχειριστούμε γεγονότα, από διάφορες πηγές. Το περιβάλλον ενσωματώνει GUI με φόρμες και στοιχεία ελέγχου που προγραμματίζουμε με απλές εντολές. Μπορούμε ταυτόχρονα να έχουμε και φόρμες (παράθυρα) και νήματα. Τέλος μπορούμε να συνδέσουμε εξωτερικά dll (συναρτήσεις) και αντικείμενα τύπου COM με ή χωρίς γεγονότα. Ο Task Manager εκτός από τα νήματα λειτουργεί τα μηνύματα MIDI (για 16 φωνές) και ουρές για επικοινωνίες με named pipes (μπορούμε να συνδέσουμε πολλά περιβάλλοντα Μ2000, που τρέχουν σε έναν υπολογιστή, ή ένα τοπικό δίκτυο, μέσω named pipes).
Το περιβάλλον έχει ενσωματωμένα αντικείμενα για διαχείριση εξωτερικών προγραμμάτων μέσω stdin stdout, καθώς και επικοινωνία για σειριακές πόρτες.
Δεν χρειάζονται εγκαταστάσεις βιβλιοθηκών γιατί οτιδήποτε χρησιμοποιεί το περιβάλλον είτε είναι ενσωματωμένο στο κώδικά του είτε το έχει μόνιμα το σύστημα των Windows.


Παλαιό κείμενο:

Θα ακολουθήσουν μερικές αναρτήσεις  για το πώς δουλεύει η Μ2000 εσωτερικά, πως δουλεύει ο διερμηνευτής, πως αποθηκεύονται οι μεταβλητές, οι πίνακες, τα αντικείμενα, γενικά όλα τα μέρη της γλώσσας μέσα από το πρόγραμμα που έχει γραφτεί σε Visual Basic 6.

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

Μια βασική συνάρτηση εκτέλεσης στο κώδικα του διερμηνευτή της Μ2000 είναι η Execute, (γραμμένη σε Visual Basic 6). Στη Vb6 ότι δεν αναφέρεται by value ή δεν είναι σε παρενθέσεις είναι πέρασμα by reference.

VB6 code
Function Execute(bstack As basetask, b$, Once As Boolean, Optional linebyline As Boolean, Optional loopthis As Boolean = False) As Long

Παρατηρούμε ότι η Execute γυρνάει ένα ακέραιο, ότι δέχεται ένα αντικείμενο τύπου basetask, καθώς και τρεις σημαίες, οι δυο είναι Optional, δηλαδή ενδέχεται να κληθεί η συνάρτηση χωρίς να δοθούν ειδικά τιμές, και η loopthis έχει εξορισμού την τιμή False.

Ο κώδικας που θα εκτελεστεί είναι το b$ και στην επιστροφή θα έχει αλλάξει. Δηλαδή μπορεί να έχει μήνυμα ή κώδικα. Αυτό καθορίζεται από τον αριθμό που επιστρέφει η Execute.

Εσωτερικά η execute διαχειρίζεται τις δομές επανάληψης, και βρίσκει αν μια εντολή είναι αναγνωριστικό και καλεί την εντολή (με έναν από τους δυο τρόπους, απευθείας με δείκτη σε συνάρτηση ή με κλήση της Identifier. Παρατηρούμε ότι η Identifier δέχεται και αυτή το αντικείμενο εκτέλεσης, το what$ που είναι η εντολή που θα εκτελέσει,  το rest$ που είναι ο κώδικας από το  b$ του Execute (ότι έχει απομείνει), μετά δυο ακόμα η nocom που αν είναι True λέει στην Identifier να μην το ψάξει, έχει κλήση σε τμήμα χρήστη (δεν υπάρχει στα selecet case) και το Lang που του λέει σε τι γλώσσα είναι εντολή (αγγλικά είναι το 1 και ελληνικά το 0), και αυτό έχει βρεθεί πιο πριν.
(παλαιότερα μια Print θα βρίσκονταν στο κώδικα, και θα έμπαινε στην identifier για να εκτελεστεί. Στην 8.1 έκδοση αυτό δεν συμβαίνει άμεσα, αλλά αν δώσουμε @Print τότε θα συμβεί! Αυτό γίνεται γιατί μπορούμε να φτιάξουμε ένα τμήμα με το όνομα Print, και το πετυχαίνει ο διερμηνευτής, αλλάζοντας το όνομα της Print στον εσωτερικό πίνακα αναζήτησης! Έτσι η Print δεν θα βρεθεί στις εντολές και θα αναζητηθεί στα τμήματα:
            Τμήμα Γενικό Τύπωσε {
                  @Τύπωσε "οκ"
            }
            Τύπωσε
            @Τύπωσε "όλα καλά"

Ορισμένες εντολές δεν έχουν περάσει στην αυτόματη κλήση (θα δούμε πώς γίνεται αυτή), και χρησιμοποιείται ο "παλιός" τρόπος. (σε μετρήσεις που έχω κάνει ο παλιός τρόπος δεν είναι πιο αργός. Αν και ο "γρήγορος" βρίσκει το όνομα και άμεσα την διεύθυνση της ρουτίνας που θα καλέσει -μέσω μιας συνάρτησης μεσολάβησης, ή proxy στην ορολογία των υπολογιστών, η κλήση έχει ένα πέναλτι λόγω αλλαγής της cache μνήμης του επεξεργαστή, για να μεταβεί σε άλλη περιοχή κώδικα. Η αναζήτηση στο μακρύ "select case" γίνεται στην ίδια περιοχή κώδικα. Παρόλα αυτά η "γρήγορη" μέθοδος σε κάποιους επεξεργαστές με λιγότερο εκμεταλλεύσιμη cache μνήμη θα κερδίζει.(ή πολύ μικρότερη ή πολύ μεγαλύτερη από ένα όριο, θα δίνει κέρδος, και αυτό συμβαίνει γιατί στην πολύ μεγάλη θα είναι μέσα στη cache μνήμη και η ρουτίνα, που βρίσκεται μακριά και κάνει αρχικά τη ζημιά, και στην άλλη περίπτωση, στη πολύ μικρή cache, θα έχουμε τμηματική παραλαβή του select case, άρα χάσιμο του όποιου πλεονεκτήματος). Η γρήγορη εκδοχή, θα είναι γρήγορη ακόμα και με μικρή cache (μιλάμε για την ελεύθερη, όχι κατ' ανάγκη για το σύνολό της).


VB6 code
Function Identifier(basestack As basetask, what$, rest$, Optional nocom As Boolean = False, Optional lang As Long = 1) As Boolean


Η Execute διαχειρίζεται και τις εντολές καταχώρησης-ανάθεσης σε μεταβλητή. Όταν διαβάζει ένα όνομα βρίσκει τι γενικός τύπος είναι, μέσω μια συνάρτησης που καλεί απευθείας με μια Select Case
Υπάρχουν αρκετά νούμερα που μπορεί να γυρίσει, επιπλέον γυρνάει στο w$ αυτό που μπορεί να βρήκε σε κεφαλαία γράμματα (ενδέχεται να μην βρει κάτι, γιατί είναι ένας χαρακτήρας στην αρχή μη αναγνωρίσιμος), στο ss$ γυρνάει ότι βρήκε χωρίς αλλαγές (χρησιμοποιείται ορισμένες φορές όπως στην κλήση ρουτινών, το όνομα όπως γράφτηκε, δηλαδή να ξεχωρίζουν πεζά με κεφαλαία και με γράμματα με τόνους) και τέλος η γλώσσα (μας δίνει σε τι γλώσσα είναι η εντολή, ελληνικά ή αγγλικά) και έχει σημασία γιατί ορισμένες εντολές δέχονται επιπλέον αναγνωριστικά και ζητάνε μόνο αυτά με την ίδια γλώσσα με την εντολή που χρησιμοποιήθηκε.

VB6 code
Select Case IsLabelDotSub(HERE$, b$, w$, ss$, lang)
...(επεξεργασία)

Δεν έγραψα παραπάνω για την HERE$. Αυτή είναι μια γενική μεταβλητή. Έτσι ξεκίνησε από την πρώτη έκδοση και παραμένει μέχρι και την έκδοση 8. Αυτό που γράφεται σε αυτήν είναι το NameSpace δηλαδή το όνομα χώρου όπου δημιουργούνται μεταβλητές. Αν δημιουργήσουμε μεταβλητή στην εισαγωγή εντολών (στο ">_" που βγαίνει στην κονσόλα), τότε αφενός θα κληθεί η Interpreter μια light έκδοση του Execute, με Here$="" δηλαδή το γενικό ή global. Αν φτιάξω την Α τότε θα έχω στη λίστα μεταβλητών την Α. Αν όμως καλέσω ένα τμήμα Α και φτιάξω εκεί την Α θα έχω την Α.Α. Η Μ2000 δεν φτιάχνει τεράστια ονόματα για όνομα χώρου. Έχει έναν τρόπο περιορισμού τους. Όμως σε ορισμένες κλήσεις που έχουμε κλήση σε ιδιότητα αντικειμένου το όνομα της ιδιότητας μπορεί να έχει τρία συστατικά, το όνομα τμήματος, το όνομα αντικειμένου, και αυτό της ιδιότητας. Ουσιαστικά αυτό που γίνεται είναι ότι το HERE$ κρατάει τα δυο πρώτα και όταν τρέχει ο κώδικας κάθε μεταβλητή αναζητείται πρώτα στο HERE$+"."+όνομα$ ή αν έχει μια τελεία στο HERE$+όνομα$, ή αν έχει πολλές τελείες στην αρχή (γίνεται και αυτό) τότε ψάχνει την αντιστοιχία σε εσωτερική λίστα στο αντικείμενο εκτέλεσης τύπου Basetask.


Για το αντικείμενο εκτέλεσης (κλάση Basetask)
Το αντικείμενο εκτέλεσης στις πρώτες εκδόσεις της γλώσσας ήταν το αντικείμενο εισόδου/εξόδου (π,χ, κονσόλα) ή μόνο εξόδου (π.χ. εκτυπωτής). H Execute και η Identifier όταν εκτελούνται "γνωρίζουν" εκτός από το κώδικα και το που τρέχουν, με τι είσοδο και έξοδο έχουν να κάνουν.

Αυτά ακριβώς, η είσοδος- έξοδος, ο κώδικας και η επιστροφή κατάστασης είναι βάση της Μ2000 και κάθε γλώσσας. Η Μ2000 μπορεί να τα αλλάζει πολύ εύκολα με μπλοκ που κάνουν την αλλαγή. Π.χ.
Αν έχω μια φόρμα έστω Φόρμα1 μπορώ να οδηγήσω εντολές στη Φόρμα1 με επιλογή επιπέδου. Μπορώ να τυπώνω στο βασικό επίπεδο (στη κονσόλα της Μ2000)  μέσα από άλλο με την Επίπεδο {} χωρίς όνομα δηλαδή. Στο παράδειγμα δεν χρησιμοποιούμε εξωτερικό γεγονός για να καλεί συναρτήσεις εξυπηρέτησης μηνυμάτων, και το εσωτερικό κάνει το εξής παίρνει το μήνυμα (στα αγγλικά είναι) και το ενώνει με το όνομα της φόρμας και με το όνομα του τμήματος Έτσι εμείς έχουμε φτιάξει έστω στο τμήμα Α την Φόρμα1.Click και αυτή θα υπάρχει ως Α.Φόρμα1.Click. Δείτε τι γίνεται όμως: Η κλήση της γίνεται με ένα ρυθμιστή "τοπικά"

Όρισε Φόρμα1 Φορμα
Επίπεδο Φόρμα1 {
      Παράθυρο 12, 16000,8000
      Οθόνη 2
      Πένα 15
      Φόρμα 40,20
      Τύπωσε @(0,Ύψος/2), $(6, Πλάτος), "Κεντραρισμένο Κείμενο"
}
\\ άνοιγμα παραθύρου Modal, με αναμονή για κλείσιμο, για να προχωρήσει
Συνάρτηση Φόρμα1.Click {
      \\ η συνάρτηση εξυπηρέτησης μηνύματος καλείται ως τοπική
      \\ δηλαδή με αλλαγή του ονόματος με το όνομα του τμήματος
      \\ που βρίσκεται αυτή!
      \\ για το λόγο αυτό "διαβάζεται" η Φόρμα1
      Α=Ρώτα("οκ?","Μήνυμα")
      Τύπωσε Α \\ τυπώνει στο βασικό επίπεδο
      \\ τυπωνουμε στη φόρμα
      Επίπεδο Φόρμα1 { Τύπωσε Α : Ανανέωση}
      Ανανέωση
}
Μέθοδος Φόρμα1,"Show", 1
Όρισε Φόρμα1 Τίποτα


Δείτε τη συμβαίνει με Κάλεσε Τοπικά: Η Α υπάρχει στο τμήμα και η κλήση Κάλεσε Τοπικά κάνει την συνάρτηση να έχει το ίδιο HERE$ με το τμήμα οπότε η Α βρίσκεται και αυξάνει τιμή κατά ένα.
Παρόλο που το HERE$ αλλάζει, παραμένει κάτι άλλο που δεν αλλάζει και αυτό είναι ότι κάθε νέο που θα φτιαχτεί στην συνάρτηση θα διαγραφεί. Αν ήθελα να έχω μια Α που να μην άλλαζε την Α στο τμήμα τότε θα την είχα ως Τοπική Α. Δείτε ότι η κλήση της συνάρτησης γίνεται χωρίς να ζητάμε επιστροφή τιμής. Ειδικά η επιλογή "Τοπικά" στην κάλεσε σημαίνει ότι την καλούμε ως Κενή, και αν αυτή γυρνάει τιμή τότε θα πεταχτεί (διαφορετικά θα την μετρούσε ως λάθος)

Α=10
Συνάρτηση ΜιαΤοπική {
      Α++
}


Κάλεσε Τοπικά ΜιαΤοπική()
Τύπωσε Α

θα δώσει 11 (αν από λάθος καλέσουμε την ΜιαΤοπική με το Κάλεσε Τοπική και όχι Τοπικά τότε δεν θα έχουμε πρόβλημα στη κλήση αλλά στο αποτέλεσμα, θα πάρουμε το 10. Το γράφω εδώ γιατί είναι ένα Bug...που μπορεί να συμβεί για ένα γράμμα!
(το Κάλεσε Τοπική ΜιαΤοπική() θα πετάξει το κάλεσε και θα εκτέλεσει το Τοπική ΜιαΤοπική() η οποία ισοδυναμεί με Πϊνακας ΜιαΤοπική() για τοπικό πίνακα, να γιατί δεν βγαίνει λάθος)



Ενώ το παρακάτω θα δώσει 10

Α=10
Συνάρτηση ΜιαΤοπική {
      Τοπική Α
      Α++
}


Κάλεσε Τοπικά ΜιαΤοπική()
Τύπωσε Α


το οποίο είναι ίδιο με αυτό, στο οποίο καλούμε κανονικά μια συνάρτηση (προσοχή αν η κάλεσε καλέσει συνάρτηση που αυτή γυρίζει μη μηδενικό αποτέλεσμα τότε το αποτέλεσμα θα λογαριαστεί ως αριθμός σφάλματος, Αν θέλουμε πράγματι να καλέσουμε μια συνάρτηση που δεν είναι κενή τότε το δηλώνουμε Κάλεσε Κενή ΜιαΤοπική() και θα αγνοηθεί το όποιο αποτέλεσμα).
Το HERE$ μέσα στην Συνάρτηση αλλάζει, γίνεται τοπικό. Μπορούμε να δούμε τι γίνεται με την μεταβλητή Τμήμα$ (και η συνάρτηση είναι είδος τμήματος).

Α=10
Συνάρτηση ΜιαΤοπική {
      Α=0
      Α++
      Τύπωσε Τμήμα$
}
Κάλεσε ΜιαΤοπική()
Τύπωσε Α



Μια άλλη περίπτωση κλήσης συνάρτησης με την κάλεσε είναι με χρήση της επιστροφής ως μήνυμα λάθους:

Συνάρτηση ΜιαΤοπική {
      Διάβασε Χ
      Αν Χ<5 τότε =1 : έξοδος
      Τύπωσε "Χ=";Χ
}
Δες Οκ {
      Κάλεσε ΜιαΤοπική(2)
}
Αν όχι Οκ τότε τύπωσε λάθος$
Κάλεσε ΜιαΤοπική(10)


Το λάθος συμβαίνει στην γραμμή Κάλεσε ΜιαΤοπική(2) και όχι μέσα στην συνάρτηση. Απλά μέσα στην συνάρτηση χρησιμοποιούμε την επιστροφή τιμής για "σηκώσουμε" το λάθος.
Δείτε παρακάτω μια παραλλαγή με τμήμα. Η επιστροφή λάθους δίνεται περιγραφικά. Ο διερμηνευτής καλεί το τμήμα ως συνάρτηση (αν και δεν θα πάρει επιστροφή τιμής). Η διαφορά είναι στο Here$ όπου δεν θα υπάρχει το όνομα ΜιαΤοπική αλλά το A[1] (το Α από το τμήμα Α που έχω τον παρακάτω κώδικα). Το Here$ το διαβάζω με την Τμήμα$ (δουλεύει και σε συνάρτηση)

Τμήμα ΜιαΤοπική {
      Διάβασε Χ
      Αν Χ<5 τότε Λάθος "Τιμή μικρότερη από 5"
      Τύπωσε "Χ=";Χ, Τμήμα$
}
Δες Οκ {
      Κάλεσε ΜιαΤοπική, 2
}
Αν όχι Οκ τότε τύπωσε λάθος$
Κάλεσε ΜιαΤοπική, 10



Αν καλέσω την ΜιαΤοπική ως τμήμα (παραμένει το όνομα ΜιαΤοπική), τότε το λάθος δεν θα μπορέσει να βγει άμεσα! Η οκ στην Δες (Try στο αγγλικό ανάλογο), παίρνει τιμή 0 αν το λάθος συμβαίνει στο επίπεδο του μπλοκ. Εδώ συμβαίνει πιο μέσα, οπότε ελέγχουμε μια πιο γενική μεταβλητή τη Λάθος η οποία μόλις διαβάζεται έχει την ιδιότητα να μηδενίζει (*)!
Επίσης το όνομα που γυρνάει η εσωτερική μεταβλητή Τμήμα$, είναι το Α.ΜΙΑΤΟΠΙΚΗ (όλα τα ονόματα τμημάτων-συναρτήσεων γίνονται κεφαλαία χωρίς τόνους).

Τμήμα ΜιαΤοπική {
      Διάβασε Χ
      Αν Χ<5 τότε Λάθος "Τιμή μικρότερη από 5"
      Τύπωσε "Χ=";Χ, Τμήμα$
}
Δες Οκ {
      ΜιαΤοπική, 2
}
\\ Αν όχι Οκ τότε τύπωσε λάθος$
Αν Λάθος τότε τύπωσε λάθος$
ΜιαΤοπική, 10



* Αν βάζαμε δυο φορές:
Αν Λάθος τότε τύπωσε λάθος$
Αν Λάθος τότε τύπωσε λάθος$

Τότε αν η Λάθος είναι αληθής θα μας δώσει μια φορά το τύπωσε λάθος$. Την επόμενη θα είναι ψευδής.

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


Τμήμα ΚάλεσεΞανα {
      Διάβασε Χ
      Τύπωσε Τμήμα$
      Αν Χ>1 τότε { Κάλεσε ΚάλεσεΞανά, Χ-1 } αλλιώς Λίστα
}


Κάλεσε ΚάλεσεΞανά 10


Το όνομα του τμήματος είναι Α[1] (αν γράφουμε στο τμήμα Α, αλλά θα ήταν Α[2] αν το γράφαμε στο τμήμα που καλούσαμε με κάλεσε από το Α)

Συνάρτηση ΚάλεσεΞανα {
      Διάβασε Χ
      Τύπωσε Τμήμα$
      Αν Χ>1 τότε { Κάλεσε ΚάλεσεΞανά(Χ-1) } αλλιώς Λίστα
}


Κάλεσε ΚάλεσεΞανά(10)


Συνάρτηση ΚάλεσεΞανα1 {
      Διάβασε Χ
      Τύπωσε Τμήμα$
      Αν Χ>1 τότε { =ΚάλεσεΞανά1(Χ-1) } αλλιώς Λίστα : =0
}


Χ=ΚάλεσεΞανά1(10)


Συνοψίζοντας:
Είδαμε ότι η γλώσσα έχει μια συνάρτηση σαν έναν εικονικό υπολογιστή που δέχεται αντικείμενο για είσοδο-έξοδο, δέχεται το κώδικα που θα τρέξει και επενεργεί σε μια γενική μεταβλητή την HERE$. Για λόγους επιθεώρησης της εκτέλεσης η execute ενημερώνει μια διαδικασία trace, ιχνηλασίας, όπου δηλώνεται ο κώδικας που τρέχει και έτσι μπορεί να μας δίνει ο διερμηνευτής την εντολή που εκτελεί στο σημείο που εκτελεί, στο κώδικα.(στο πεδίο Print της φόρμας μπορούμε να τυπώσουμε μεταβλητές που θέλουμε. Δεν μπορούμε να αλλάξουμε τιμές όμως...εκτός και αν δώσουμε για εκτύπωση συνάρτηση που παίρνει παράμετρο...(αυτό κάνει για αντικείμενα).


Στο ερώτημα πώς βρίσκει στο σημείο ο ιχνηλάτης, ενώ δεν υπάρχει "program counter" η απάντηση είναι ότι το βρίσκει από το τέλος, ξέρει δηλαδή πόσος κώδικας απέμεινε! Υπάρχει περίπτωση να μην βγάζει συμπέρασμα, διότι μπορεί να έχουμε κάνει ένθεση κώδικα. Τότε απλά μας δίνει μια κόκκινη γραμμή για να καταλάβουμε ότι "αστόχησε". Εδώ στη φωτογραφία φαίνεται ότι ξεκίνησε το β με το δοκιμή β που άνοιξε το παράθυρο του ελέγχου. (αν είχαμε γράψει Ελληνικά πριν θα άνοιγε στα Ελληνικά)
Εδώ είναι η φόρμα στα ελληνικά και είναι το παράδειγμα με το παράθυρο που γράφουμε σε δυο επίπεδα, στο παράθυρο και στη κονσόλα. Ο ιχνηλάτης δεν ακολουθεί εντολές από τις συναρτήσεις που καλούνται από γεγονότα. Δείτε ότι είναι σταματημένος στο σημείο που έχουμε ανοίξει τη φόρμα Modal. Παρόλο που έχει σταματήσει η ροή του προγράμματος εκεί. Εξακολουθούν να τρέχουν προγράμματα (πάλι δηλαδή εκτελείται η Execute) γιατί την ξεκινάνε τα μηνύματα από τη φόρμα (ή όπως θα διαβάσει κανείς και το εγχειρίδιο τεύχος 12, μπορεί να φτιάξει νήματα, δηλαδή τμήματα κώδικα που εκτελούνται κάθε ένα χρονικό διάστημα, Υπάρχουν παραδείγματα όπου νήματα και μηνύματα εκτελούνται χωρίς κανένα πρόβλημα.
Πώς ξέρει ο ιχνηλάτης ότι δεν θα ακολουθήσει ένα μήνυμα; Το ξέρει η Execute και το διαβάζει από μια ιδιότητα του αντικειμένου εκτέλεσης basetask και λέγεται IamAnEvent,
Η εντολή οθόνη 2 έδωσε το πράσινο χρώμα στην φόρμα. Η Φόρμα 40, 20 έκανε το μέγεθος του παραθύρου να παίρνει 40 χαρακτήρες επί 20 γραμμές. (ενώ δώσαμε ένα μέγεθος, η Φόρμα το έκοψε στα μέτρα της). Η εντολή φόρμα δουλεύει και για την κονσόλα (γενικά όλες οι εντολές δουλεύουν και στα παράθυρα, εκτός από τις εντολές εισόδου.



Περισσότερα σε επόμενη ανάρτηση...

Παράδειγμα με δυο φόρμες, και γράψιμο και στις δυο ταυτόχρονα!

Την "ιδέα" την πήρα από ένα ερώτημα από το Insomnia.gr, όπου κάποιος ρώταγε αν μπορούσε να πάρει κείμενο που μπαίνει σε άλλη φόρμα, σε java. Εδώ στη Μ2000 είναι εύκολο. Για να γίνεται κάτι τέτοιο πρέπει να ξέρουμε αν η άλλη φόρμα είναι ανοικτή. (Αλλιώς δεν μπορούμε να γράψουμε). Πώς θα ξέρουμε αν η φόρμα 2 έκλεισε; Μας το λέει η ίδια! Υπάρχει ένα μήνυμα Unload. Αυτό το μήνυμα δίνει και μια μεταβλητή αλλά εδώ δεν ασχολούμαστε με αυτήν. Αυτή η μεταβλητή αν πάρει μη μηδενική τιμή τότε ακυρώνει το κλείσιμο της φόρμας. Εδώ δεν θέλουμε κάτι τέτοιο.

Επίσης δείτε κάτι το διαφορετικό (αν δεν το έχετε δει σε άλλα παραδείγματα με φόρμες). Όταν καλείται μια συνάρτηση για να εξυπηρετήσει ένα γεγονός τότε καλείται με μια "κάλεσε τοπικά" η οποία κάνει το εξής, βάζει για όνομα το όνομα του τμήματος που έφτιαξε τη φόρμα, και έτσι την βάζει στο χώρο του τμήματος, σαν να ήταν κώδικας του τμήματος. Με αυτό τον τρόπο όλες οι μεταβλητές (άρα βλέπουμε και τα αντικείμενα που έχουμε ορίσει με τις Declare ή ελληνικά Όρισε)  Αν ορίσουμε νέες μεταβλητές τότε αυτές θα "ζήσουν" για όσο χρόνο τρέχει η συνάρτηση εξυπηρέτησης. Αυτές οι συναρτήσεις οφείλουν να τρέχουν γρήγορα. Αν θέλουμε να εκτελέσουμε κάτι ασύγχρονα, το βάζουμε σε ένα αυτόματο νήμα με την Μετά 30 { } όπου σε 30 χιλιοστά θα τρέξει (θα βρει κάποιο χώρο στο χρόνο εκτέλεσης ο διερμηνευτής και θα την καλέσει)
Αν θέλουμε κάτι να λειτουργεί περιοδικά τότε μπορούμε να χρησιμοποιήσουμε νήμα που θα τρέχει κάθε συγκεκριμένα χιλιοστά του δευτερολέπτου, ή μπορούμε να φτιάξουμε ένα Button ως χρονιστή, όπως στο παράδειγμα 12-Απρ-2014


Μπορούμε να αφαιρέσουμε την edit1.Enter (επειδή κάνει αλλαγή όταν πατάμε το enter)

Function edit1.Enter {
      if NoForm2 Else edit2.text$=edit1.text$
}

με αυτήν, η οποία μας δίνει δυο τιμές (εδώ σηκώνουμε μόνο την μία, ο σωρός έχει τις ισχνές αναφορές και  ότι αφήσουμε θα διαγραφεί στο πέρας της συνάρτησης):
Αντί να βάλουμε If Not NoForm2 Then βάζουμε απευθείας το Else χωρίς το Not..

Function edit1.ValidString {
      Read &BeforeDisplay$
      If NoForm2 Else edit2.text$=BeforeDisplay$
}

Το πρόγραμμα ορίζει δυο φόρμες, χωρίς να δώσει διαστάσεις και τίτλο (ορίζονται αυτόματα). Επίσης οι φόρμες μπορούν να μετακινηθούν αλλά δεν αλλάζουν μέγεθος (θα μπορούσαμε να ορίσουμε την ιδιότητα "Sizable" αληθής και τότε θα άλλαζε το μέγεθος, και θα μας έστελνε και μήνυμα για να αλλάξουμε μέγεθος αν θέλουμε στα στοιχεία της φόρμας (με την "move")

Αν θέλει να δει κανείς τις ιδιότητες και τα μηνύματα της φόρμας και της εισαγωγής (έτσι αναφέρεται το TextBox στα ελληνικά στη Μ2000), στο κώδικα της γλώσσας, στο GuiM2000.frm και στο GuiEditBox.cls. Τα ονόματα των ιδιοτήτων, των μεθόδων και αυτά των μηνυμάτων είναι σε μια γλώσσα στην αγγλική. Σε επόμενες αναρτήσεις θα έχω όλη τη λίστα και το πού χρησιμεύουν.


Declare form1 Form
\\ use standard size and title
Declare edit1 TextBox Form form1
Method edit1, "move", 1000,2000,6000,600
With edit1,"Vartext" as edit1.text$

Declare form2 Form
\\ use standard size and title
Declare edit2 TextBox Form form2
Method edit2, "move", 1000,2000,6000,600
With edit2,"Vartext" as edit2.text$

Method form1,"show"
Method form2,"show"
NoForm1=False
NoForm2=False
\\ these functions are call local
\\ which means they run with module's name
\\ so they are in same scope, and so NoForm1 is Known
Function form1.Unload {
      NoForm1=True
}
Function form2.Unload {
      NoForm2=True
}
Function edit1.Enter {
      if NoForm2 Else edit2.text$=edit1.text$
}
Every 100 {
      if NoForm1 Then Exit
}
Show
Declare form2 Nothing
Declare form1 Nothing

Αναθεώρηση 13, προσθήκη ταξινόμησης με φθίνουσα σειρά.

Υπάρχει εδώ  η ταξινόμιση στο σωρό απ΄ ευθείας με διάφορους τρόπους (όχι με εντολή μια και έξω όπως εδώ)

1. Μερικές διορθώσεις λαθών.

2. Προσθήκη του λεκτικού Φθίνουσα στην εντολή Ταξινόμηση:

Ειδικά για την κατάσταση, μπορούμε να δώσουμε δεύτερη παράμετρο και με 0 σημαίνει φθίνουσα, με μη μηδενική αύξουσα (Ταξινόμηση αλφα, 0 είναι ίδιο με το Ταξινόμηση Φθίνουσα αλφα)
Το θαυμαστικό στο αλφα$() σημαίνει τιμή σειρά και όχι κλειδί (για την κατάσταση αλφα)

Αναλυτής
Αδειασε
Σειρά "Πρέβεζα", "Αθήνα", "Πάτρα","Ιωάννινα","Καβάλα","Βόλος", "Καλαμάτα","Τρίπολη","Πύργος","Ναύπλιο","Ρόδος"
Σειρά "Λαμία","Λάρισα","Άρτα","Κόρινθος","Σπάρτη","Ηγουμενίτσα","Μεσολόγγι","Θήβα","Χαλκίδα"
Κατάσταση αλφα
Ενώ όχι κενό {
      Προσθήκη αλφα, γραμμα$
}      
Έγγραφο α$
Ταξινόμηση Φθίνουσα αλφα
καθαρο α$
για ι=0 έως μήκος(αλφα)-1 {
      α$=παράθεση$(αλφα$(ι!))
}
Τύπωσε α$
Ταξινόμηση αλφα
καθαρο α$
για ι=0 έως μήκος(αλφα)-1 {
      α$=παράθεση$(αλφα$(ι!))
}
Τύπωσε α$
Τύπωσε φόρτος

Τρίτη 10 Μαΐου 2016

Αναθεώρηση 11 - Μια μικρή προσθήκη.

Σε αυτήν την αναθεώρηση απλά πρόσθεσα μια ευκολία. Αν σε ένα νήμα θέλουμε να χρησιμοποιήσουμε μια μεταβλητή, για να "συγκεντρώσουμε" κάτι, πριν τερματίσει ο χρόνος εκτέλεσης του νήματος (τα νήματα εκτελούνται σε χρονικά διαστήματα, όπου το διάστημα λογαριάζεται ως χρόνος μεταξύ δυο εκκινήσεων του νήματος και όχι ως χρόνος αναμονής, στο πέρας εκτέλεσης). Αν απλά δημιουργήσουμε μια μεταβλητή αυτή θα γίνει μέρος του τμήματος που ανήκει το νήμα. Αν δημιουργήσουμε μια στατική τότε εκεί όντως μπορούμε να κάνουμε τη δουλειά μας, αλλά οι στατικές είναι αφενός οι απλές αριθμητικές, ακέραιες, αλφαριθμητικές (δεν έχουμε πίνακες), και αφετέρου παραμένουν με την τελευταία τιμή και στην επόμενη εκκίνηση του νήματος. Γενικά χρησιμοποιούμε την εντολή Τοπική και Τοπικές (το ίδιο είναι), ή Local για να δηλώσουμε ότι η μεταβλητή ή μεταβλητές ακόμα και αν έχουν ίδιο όνομα με άλλες στο τμήμα, θα γίνουν νέες.
Αν γράψουμε το παρακάτω σε ένα τμήμα τότε η List θα μας δείξει ότι υπάρχουν δυο μεταβλητές με το ίδιο όνομα. Η παλιά δεν πρόκειται να διαβαστεί ποτέ. Όταν το τμήμα τερματίσει θα σβηστούν.

Local A=10
Local A=4
List


Αν σε ένα νήμα χρησιμοποιούσαμε την Local Α, ακόμα και μια φορά τότε κάθε φορά που θα έτρεχε θα έφτιαχνε μια νέα Α.
Ένας τρόπος να χρησιμοποιήσουμε τοπικά με την έννοια, για προσωρινή χρήση είναι σε μια Για {}  ή For {}, η οποία μπορεί να είναι για κάποιο αντικείμενο ή για το Αυτό  (το αντικείμενο του τμήματος που τρέχει). Δείτε παρακάτω ότι οι Α και Β() που ορίσαμε μέσα στην Για Αυτό { } δεν υπάρχουν έξω από αυτό (στη δεύτερη εντολή Λίστα φαίνεται η πρώτη Α μόνο)

Τοπική Α=10
Για Αυτό {
      Τοπικές A=4, Β(20,5)=12
      Τύπωσε Α, Β(1,1)
      Λίστα
}
Τύπωσε Α
Λίστα


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

Α=5
Β=40
Μετά 30 { Τοπική Α=10 : Τύπωσε Α*Β : Λίστα }
Αναμονή 100
Λίστα


Το ίδιο πρόγραμμα θα έτρεχε και σε προηγούμενη αναθεώρηση αλλά εδώ η Λίστα μετά την Αναμονή (όπου στο 0,1 του δευτερολέπτου, και ειδικότερα στο 0,03 αυτού θα τρέξει το νήμα - είναι αυτόματο νήμα, με την Μετά) θα έχει μόνο μια Α και μια Β
Μπορούμε να χρησιμοποιήσουμε μόνο μια φορά τη λέξη Τοπική ακόμα και χωρίς μεταβλητές και θα πάρει "σήμα" το νήμα ότι θα καθαρίσει στο τέλος της χρονομερίδας εκτέλεσης.( Η Μετά ή After τρέχει μια μόνο φορά, αλλά άλλα νήματα τρέχουν για όσο θέλουμε)

Μια παραλλαγή που ήδη έτρεχε είναι αυτή με χρήση στατικής:

Α=5
Β=40
Μετά 30 { Στατική Α=10 : Τύπωσε Α*Β : Α-- : Λίστα }
Μετά 60 { Στατική Α=10 : Τύπωσε Α*Β : Α-- : Λίστα }
Αναμονή 100
Λίστα


Η στατική Α είναι ανά αντικείμενο εκτέλεσης και εδώ τα νήματα έχουν ξεχωριστό αντικείμενο.
Η χρησιμότητά της φαίνεται εδώ, όπου κάθε φορά που τρέχει το νήμα η Α μειώνεται κατά 1 (η αρχική τιμή 10 δίνεται μια φορά):

Α=5
Β=40
Λίστα
Νήμα { Στατική Α=10 : Τύπωσε Α*Β : Α-- : Λίστα } ως κάτι κάθε 30
Αναμονή 100
Λίστα
Τύπωσε Α=5


Μέσα στο χρόνο των 100 χιλιοστών του δευτερολέπτου το νήμα θα τρέξει τρεις φορές. Θα μειώσει τρεις φορές το Α ενώ θα έχει δώσει τρία αποτελέσματα. Ο στατικές έρχονται πρώτες στην αναζήτηση, και μετά ακολουθούν οι τοπικές και μετά οι γενικές. Αν δεν ορίσουμε κάτι έχουμε δήλωση τοπικής.
π.χ. σε ένα τμήμα αν εμφανιστεί αυτό:
Α=5
τότε αν υπάρχει γενική Α θα σκιαστεί από την τοπική που θα φτιαχτεί άμεσα. Αν υπάρχει Α θα αλλάξει τιμή, θα πάρει το 5, αν υπάρχει στατική τότε θα πάρει η στατική τιμή.
Α<=5 δίνουμε αν θέλουμε η τιμή να πάει στην γενική Α (και να μην δημιουργηθεί τοπική).
Αν ορίσουμε Γενική Α=10 ενώ υπάρχει γενική Α τότε γίνεται ότι και με τις τοπικές, η νέα γενική από το σημείο που μπήκε στο "σύστημα" θα μετράει σαν γενική μέχρι να διαγραφεί στο τέλος του τμήματος όπου δημιουργήθηκε. Αν τρέχει ένα νήμα πριν, σε άλλο επίπεδο, και αναζητεί την Α θα βρει την παλιά γενική Α.

Για τις ισχνές αναφορές (αυτό δεν άλλαξε στην 11, απλά εδώ το αναφέρω γιατί ταιριάζει με τις πληροφορίες για τις τοπικές-γενικές-στατικές, και εδώ οι δείκτες)

Όλες οι μεταβλητές εμφανίζονται ως γενικές αν χρησιμοποιήσουμε δείκτες.(οι στατικές δεν παίρνουν δείκτες, είναι σε άλλη λίστα - δεν εμφανίζονται και με την Λίστα). Εδώ η β$ κρατάει μια ισχνή αναφορά στην άλφα. Την λέμε ισχνή γιατί ναι μεν είναι αναφορά αλλά η πραγματοποίηση της σύνδεσης γίνεται όταν την χρησιμοποιούμε (και μπορεί να αποτύχει, αν κρατήσουμε την αναφορά ή την επιστρέψουμε από το τμήμα αφού τερματίσει και τότε η αλφα θα έχει διαγραφεί)

αλφα=10
β$=ισχνή$(αλφα)
β$.++
Τύπωσε εκφρ(β$), β$
\\ σαν γενική
β$.<=100


Τύπωσε εκφρ(β$), β$
Λίστα
\\ η χρήση δείκτη δεν δημιουργεί μεταβλητή
\\ μπορούμε να χρησιμοποιήσουμε και το απλό =
β$.=105
Λίστα


Μια κανονική αναφορά είναι αυτή εδώ:
αλφα=10
Ένωσε αλφα στο βήτα
Λίστα
βήτα+=100
Λίστα


Η οποία χρησιμοποιείται έτσι:

αλφα=10
Τμήμα Κοίταμε {
      Διάβασε &βητα
      βήτα+=100
      Λίστα
}
Κοίταμε &αλφα
Λίστα


Η εντολή Ένωσε κάνει διάφορα και ένα από αυτά είναι να βγάλει νέο αναφορά για κάτι, ακόμα και αν είναι στο ίδιο τμήμα! Το &αλφα στην "αποστολή" είναι μια ισχνή αναφορά, ενώ το στο Διάβασε λέμε στο διερμηνευτή να "σηκώσει" από το σωρό τιμών μια αναφορά και να την ενώσει με το βήτα.
Στο παρακάτω τα &αλφα, ισχνή$(αλφα) είναι ίδια (είναι η περίπτωση εδώ, αλλά η ισχή$() κάνει πράγματα που δεν κάνει το απλό &). Στη διάβασε εντός της Κοίταμε θα πάρουμε μια αναφορά κανονική στο βητα και μια ισχνή. Εδώ αυξάνουμε την αλφα με χρήση της ισχνής και βλέπουμε με τη Λίστα ότι και η βήτα άλλαξε τιμή (αφού όλα δείχνουν στο ίδιο, απλά η βήτα$ δείχνει κατά τη χρήση της, οπότε στο Λίστα δεν φαίνεται να έχει τιμή αλλά έχει το πλήρες όνομα της άλφα)

αλφα=10
Τμήμα Κοίταμε {
      Διάβασε &βητα, βήτα$
      βήτα$.+=100
      Λίστα
}
Κοίταμε &αλφα, ισχνή$(αλφα)
Λίστα




Αναθεώρηση 10.

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

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



Form 80,50
Report {Test for static variables in modules in groups
            Static variables are bind to execute object
            Each module/Function can be run in different execution object
            But allways  a series of specific execution objects  occur
            So if A call B then  B has allways the same static variables
            But if C call B then B use another set of static variables
            So in group K module b, static k first time get value 1000
            If any new execution object call this module then an new K created
            }
Call Clear \\  clear all variables including static - hide this and execute this module two or more times.
module M {
      Read &MM
      call MM.b
      modules ?
}
Group k {
      module b {
            read klm
            static k=1000
            k=k+1
            print format$("a({1}).b.k={0}", k , klm)
      }
      module c {
            read klm
            static k =5000
            k=k+1
            if klm>=0 then {
            print format$("a({1}).c.k={0}", k , klm)
            } else {
            print format$("kk.c.k={0}", k)
            }
      }
}
dim a(10)
a(2)=k
a(4)=k
for i=1 to 2 : a(2).b 2: next i
for i=1 to 2 : a(4).b 4: next i
for i=1 to 5 : a(2).c 2 : next i
for i=1 to 5 : a(2).c 2 : next i
for i=1 to 5 : a(4).c 4 : next i
a(5)=a(2)
for i=1 to 5 : a(5).c 5 : next i
kk=a(5)
for i=1 to 5 : kk.c -1 : next i
module aaa {
      read &klm
      \\ static change because we are in a new process
      klm.c -1
}
Function moufa {
      Print "static is the same because we are in same process"
      \\ it is a fake function, is like Sub, but Function is in a list o functions, and can change definition
      \\ but a Sub is code, and every time we call interpereter search from the end to start.
      \\ Subs use all memory for retrun stack, so we can do nice things also.
      kk.c -1
      Print KM
}
      Print "New process object"
aaa &kk
checkme()
Print  "ok, look lazy now"
KM=100
aa$=Lazy$(&moufa())
link weak aa$ to aa()


For i=1 to 2 {
  Call aa()
}
Print "continue in aaa"
aaa &kk
Sub checkme()
            Print "static is the same because we are in same process"
            kk.c -1
End Sub