Κυριακή, 31 Ιανουαρίου 2016

Σάββατο, 30 Ιανουαρίου 2016

Παράδειγμα Λουλούδια 2

Στις 26/10/2015 είχε δημοσιεύσει το πρώτο παράδειγμα με τις περιστρεφόμενες καμπύλες. Σήμερα έχω ένα ακόμα όπου τις μεταφέρουμε όπου θέλουμε με το ποντίκι ενώ παράλληλα γράφουμε αριθμούς στην οθόνη.
Χρησιμοποιώ τη Διέκοψε για να βγω από την φόρμα. Επίσης χρησιμοποιώ την Δείκτη.μορφή για να κρύψω το ποντίκι πάνω στην οθόνη (το επαναφέρω στο τέλος). Επίσης ενώ έχω δείξει πως χωρίζουμε την οθόνη για να κρατάμε κάποιες γραμμές στο πάνω μέρος χωρίς ολίσθηση, με την εντολή Οθόνη που σβήνει το κάτω μέρος, σήμερα έχω την εντολή Κύλιση που θέτει το χώρισμα στην γραμμή (εδώ η 4 είναι η πέμπτη γραμμή άρα κρατάμε από 0 έως 3, άρα τέσσερις γραμμές).
Δώστε βοήθεια Κύλιση (με αυτή την εντολή μπορούμε επιπλέον να κάνουμε ανάποδη ολίσθηση, να προσθέτουμε γραμμές από πάνω, δείτε τι έχει στη βοήθεια)



Στο παράδειγμα οι μεταβλητές ορίζονται όταν θέλουμε να τις χρησιμοποιήσουμε. Αυτή είναι μια ευκολία που είχε η Basic, και έχει και η M2000. Συνήθως αυτή η πρακτική δεν είναι καλή σε άλλες γλώσσες. Εδώ όμως επειδή μας χρωματίζει τα δικά μας αναγνωριστικά ο διορθωτής έχουμε άμεση εποπτεία του τι έχουμε ορίσει. Εννοείται πως αν βρει ο διερμηνευτής ένα άγνωστο αναγνωριστικό στην δεξιά πλευρά της αντικατάστασης τότε έχουμε λάθος.
Δεν υπάρχει καθόλου αναμονή. Δίνουμε μόνο μια ανανέωση 1000 που σημαίνει, κάνε τώρα ανανέωση οθόνης και βάλε το μετρητή στο ένα δευτερόλεπτο (ώστε μετά από αυτό να κάνεις πάλι ανανέωση). Ουσιαστικά πριν περάσει το δευτερόλεπτο πάλι κάνουμε ανανέωση και ρυθμίζουμε το μετρητή. Κάθε σχήμα τυπώνεται με XOR έτσι την πρώτη φορά τυπώνεται:

Νέα_Οθόνη=Σχήμα XOR Τωρινή_Οθόνη 

και μετά η οθόνη επανέρχεται επειδή τυπώνουμε πάλι το ίδιο από πάνω!

 Νέα_Οθόνη1= Σχήμα XOR Νέα_Οθόνη

Η Νέα_Οθόνη1 είναι η Τωρινή_Οθόνη δηλαδή παίρνουμε την αρχική.  Αμέσως μετά τυπώνουμε και πιθανόν να ολισθήσει το κάτω μέρος της οθόνης. Απλά ο μετρητής της Ανανέωση δεν θα επιτρέψει να δούμε την ολίσθηση πριν σχεδιάσουμε ξανά την Nέα_Οθόνη. 
Πώς τα καταφέρνει η Μ2000 και δεν αφήνει να ανανεωθεί η οθόνη; Αυτό δεν είναι μυστικό, αλλά είναι γραμμένο στο κώδικα της Μ2000 που είναι ανοιχτός (το exe αρχείο έρχεται μαζί με το κώδικα του περιβάλλοντος της γλώσσας)


Οθόνη 1,0
Φόντο 1,2 ' από μπλε μέχρι πράσινο
Πένα 13
Διπλά
Χρώμα 4,2 { \\ 2 για περίγραμμα μόνο
      Τύπωσε "Μ2000",,"Λουλούδια 2"
}
Κανονικά
Πένα 15
κκ=0
Κύλιση Χώρισμα 4
Δείκτη.μορφή Κρύψε
Επανέλαβε {
      Για ι=Πι/10 Έως Πι Ανά Πι/2 {
            μια_τιμή=Τυχαίος(2,15)
            μεταβλητή1=-3900
            μεταβλητή2=-3600
            Για Λ=0 Έως 2*Πι-Πι/32 Ανά Πι/32 {
                  κκ++
                  βάσηΧ=δείκτης.χ
                  βάσηΥ=δείκτης.υ
                  Αν Δείκτης>0 Τότε Δείκτη.Μορφή Δείξε : Διέκοψε
                  μεταβλητή2+=60
                  μεταβλητή1+=60
                  Χρώμα ! μια_τιμή {
                        Για Μ=0 Έως 2*Πι-Πι/8 Ανά Πι/8 {
                              Θέση βάσηΧ,ΒάσηΥ
                              α=Πι/3+Μ+Λ
                              β=2*Λ+Μ
                              γ=Πι+Μ+Λ
                              Καμπύλη Γωνία α,500,β,-3000,γ,2000
                              Θέση βάσηΧ-μεταβλητή1,βάσηυ-μεταβλητή2
                              Καμπύλη Γωνία Πι/16-α,100,Πι/16-β,-600,Πι/16-γ,400
                        }
                  }
                  Ανανέωση 1000
                  Χρώμα ! μια_τιμή { \\ το ! δηλώνει ότι θα γίνει Xor με τα περιεχόμενα της οθόνης
                        Για Μ=0 Έως 2*Πι-Πι/8 Ανά Πι/8 {
                              α=Πι/3+Μ+Λ
                              β=2*Λ+Μ
                              γ=Πι+Μ+Λ
                              Θέση βάσηΧ,ΒάσηΥ
                              Καμπύλη Γωνία α,500,β,-3000,γ,2000
                              Θέση βάσηΧ-μεταβλητή1, βάσηυ-μεταβλητή2
                              Καμπύλη Γωνία Πι/16-α,100,Πι/16-β,-600,Πι/16-γ,400
                        }
                  }
                 Τύπωσε κκ,
            }
      }
} Πάντα


Αναθεώρηση 144

Παράλληλα με την έκδοση 8.1 που ετοιμάζω, διορθώνω και ότι δω στην 8.0 οπότε να και η αναθεώρηση 144.
ένα λάθος που υπήρχε ήταν το ΑΥΤΟ (όταν το βάζαμε έτσι ΑΥΤΟ=Κλάση1()) που είχε γραφεί με λατινικούς χαρακτήρες! (πριν την 141 ήταν οκ, αλλά άλλαξα την ρουτίνα, και κατά λάθος έγραψα το ΑΥΤΟ ως AYTO ...το δεύτερο είναι με αγγλικά)



Πέμπτη, 28 Ιανουαρίου 2016

Προγραμματισμός 0007

Στο σημερινό άρθρο θα δούμε ένα πρόγραμμα που ζαλίζει! Ζαλίζει γιατί μπορούμε να κάνουμε την οθόνη μας να τυπώνει μια λέξη ή περισσότερες να τρέχουν αριστερά η δεξιά!


Το πρόγραμμα το γράφουμε σε ένα τμήμα Α π.χ. με Σ Α ανοίγει ο διορθωτής, κάνου επικόλληση το παρακάτω, πατάμε Esc, γράφουμε Σώσε π007 και σώνεται ως π007.gsb (φορτώνει άλλη φορά με Φόρτωσε π007). Όταν θέλουμε να ξανά σώσουμε πατάμε ctrl+A.

Πρώτα ρυθμίζουμε η ανανέωση να γίνεται κάθε 50 χιλιοστά του δευτερολέπτου! (δοκιμάστε κάτι άλλο).
Μετά επιλέγουμε μια στατική μεταβλητή (για να θυμάται τι έχουμε δώσει αν ξανατρέξουμε το τμήμα). Για να σβήσει η στατική μεταβλητή δίνουμε την εντολή Καθαρό πριν καλέσουμε το τμήμα.
Μετά χρησιμοποιούμε την εντολή Εισαγωγή με το χαρακτήρα ! που κάνει εισαγωγή με αναλογικούς χαρακτήρες (δηλαδή κανονικό κείμενο, όχι με σταθερό πλάτος όπως της κονσόλας). Το 32 λέει το φάρδος του πεδίου εισαγωγής. Εξ ορισμού παίρνει μέχρι 50 χαρακτήρες αλλά παίζει και έτσι:
Εισαγωγή ! γ$, 30 μήκος=60
Οπότε με το παραπάνω ορίζω μήκος 60 χαρακτήρων που μπορούν το μέγιστο να αποθηκευτούν στο γ$. Το πεδίο αυτό εμφανίζεται πάνω από την οθόνη και μετά εξαφανίζεται. Έχει και άλλες ιδιότητες. Αν αντί για αλφαριθμητικό ζητάμε αριθμό (εκεί δεν υπάρχει μήκος) τότε υπάρχει έλεγχος στο τι γράφουμε, δεν μας αφήνει να γράψουμε κάτι χωρίς αριθμό.  Με Esc ή με enter ή με βελάκια κάνουμε έξοδο από την εισαγωγή. Μπορούμε να δούμε τι έξοδο προτίμησε ο χρήστης με την εσωτερική μεταβλητη Πεδίο (δείτε το Βοήθεια Πεδίο, η εντολή Εισαγωγή ! είναι η Πεδίο για αναλογικη γραφή, έχει μικρές διαφορές, αλλά στην έξοδο είναι ίδιες)

Η εντολή τοπική (τοπικές) δεν χρειάζεται. Θα μπορούσαμε να βάζαμε μόνο το α=Αληθές, και το ν το ορίζουμε παρακάτω με τη τιμή που δίνουμε. Όπως και η εντολή Στη (αντιστοιχεί στη Let) δεν χρειάζεται, απλά τα έχουμε για ομορφιά εδώ! Έχουν την σημασία τους αλλά εδώ δεν θα εξηγηθεί παραπάνω.
Κάθε φορά που θα ξεκινάμε το πρόγραμμα θα μας εμφανίζει αυτό που θέλουμε να χειριστεί, και μετά θα βλέπουμε το αποτέλεσμα. Τερματίζει το πρόγραμμα αν πατήσουμε το διάστημα (αριθμός 32) ή αν πατήσουμε Esc (δεν φαίνεται αλλά με αυτό κάνουμε escape - απόδραση). Φυσικά τερματίζει αν το μήκος είναι μικρότερο από 2 (δεν έχει νόημα).


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

Η δομή Κάθε { } κάνει επανάληψη του μπλοκ { } κάθε 50 χιλιοστά του δευτερολέπτου (το λιγότερο). Βγαίνουμε με εντολή Έξοδος.

Η αριθμητική συνάρτηση Τυχαίος δίνει ένα τυχαίο αριθμό μεταξύ δυο άκρων (και τα δυο άκρα συμπεριλαμβάνονται σε αυτό).

Εδώ μαθαίνουμε για την χρήση της Μεσ$() ή Mid$() και της Αρισ$() ή Left$(). Η Μεσ$() παίρνει και τρίτη παράμετρο, τον αριθμό χαρακτήρων που θέλουμε. Αν δεν δώσουμε, όπως εδώ, τότε μας γυρνάει όλους τους χαρακτήρες, από τη θέση που δηλώνει η δεύτερη παράμετρος.

Η συνάρτηση πατημένο() δίνει αληθές αν την στιγμή του ελέγχου είναι πατημένο ένα πλήκτρο (την έχουμε δει την εντολή στο πρόγραμμα του πιάνου). Το νούμερο είναι το νούμερο του πλήκτρου (αν και εδώ τυχαίνει να είναι το ASCII νούμερο του διαστήματος). Τα νούμερα αυτά λέγονται Virtual Key Codes   και στη σελίδα της Microsoft . Π.χ. για το κουμπί Home λέει:


VK_HOME
0x24
Το 0x24 είναι δεκαεξαδικό νούμερο. Η Μ2000 τα διαβάζει όπως είναι (με το 0χ ή 0x). Η Τύπωσε βγαίνει και σε δεκαεξαδκό, ως Δεκαεξ δηλαδή Δεκαέξ 12*30 θα δώσει αποτέλεσμα σε δεκαεξαδικό (όχι αρνητικούς, δίνει σύμβολο λάθους ???- και όχι πάνω από 0xFFFFFFFF γιατί δίνει λάθος ???+). Μπορούμε να γράψουμε Δεκαεξ 0xA120+0xFF00 και να προσθέσουμε δυο unsign (χωρίς πρόσημο)  δεκαεξαδικούς. Οι μεταβλητές double (πραγματικές) και  ακέραιες με % στο τέλος του ονόματος (double εσωτερικά) μπορούν να κρατούν unsign δεκαεξαδικούς. Η συνάρτηση Δεκαεξ() δίνει τον αριθμό ως αλφαριθμητικό, χωρίς το 0x μπροστά. Μπορεί μάλιστα να δώσει 1,2,3 ή 4 Byte δεκαεξαδικό, αν το βάλουμε ως δεύτερη παράμετρο (εξ ορισμού είναι το 4).

Το ~ εδώ α~ κάνει το αληθές ψευδές ή το ψευδές αληθές στην μεταβλητή α...Χωρίς αυτό θα θέλαμε α = όχι α
Αυτό σημαίνει ότι ο διερμηνευτής θα αναζητούσε δύο φορές την μεταβλητή, αλλά με το α~ την αναζητεί μια.
Υπάρχουν και άλλες συντόμευσεις.
αν κ=10 η κ-! κάνει τη κ ως -10 δηλαδή κάνει αλλαγή προσήμου
Προσοχή το ~ στα αλφαριθμητικά είναι το like δηλαδή:
Τύπωσε "alfa" ~ "a*"δίνει -1 (αληθές)
(τα ++ και -- σε έκφραση γίνονται + και +, ενώ αν έχουμε μια μεταβλητή έστω κ με κ++ αυξάνουμε κατά ένα ενώ με --μειώνουμε κατά ένα, υπάρχουν και τα +=, -=, *=, /=)

Ανανέωση 50
Τοπικές α=Αληθές, ν=0
Στατική γ$="Το όνομά σου "
Εισαγωγή ! γ$, 30
Στη ν=Μήκος(γ$)
Αν ν<2 Τότε Έξοδος
Κάθε 50 {
      Αν Τυχαίος(1,80)<4 Τότε α~
      Αν α Τότε {
            Στη γ$= Μεσ$(γ$,ν)+Αρισ$(γ$,ν-1)
      } Αλλιως {
            Στη γ$= Μεσ$(γ$,2)+Αρισ$(γ$,1)
      }
      Τύπωσε Μερος $(0),γ$+γ$+γ$+γ$+γ$
      Τύπωσε
      Αν Πατημένο(32) Τότε έξοδος
}





Τρίτη, 26 Ιανουαρίου 2016

Προγραμματισμός 0006

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

Στο παρακάτω πρόγραμμα φτιάχνουμε δυο συναρτήσεις, μια που υπολογίζει με τύπο το στοιχείο Χ της ακολουθίας Φιμπονάτσι, αλλά και με άλλο τρόπο, με επανάληψη (Ένώ  { } ).
Την συνάρτηση με τον ένα τύπο την γράφουμε με την Κάνε όπου μεταγράφει εσωτερικά την συνάρτηση έτσι: Συνάρτηση Ακ.Φιμπ {Διάβασε χ :  = ακ(απολ(Συζυγής_ΧΑ^χ-(-Συζυγής_ΧΑ^(-χ)))/ρ5) }

Στην δεύτερη συνάρτηση χρησιμοποιώ τον σωρό τιμών. Κάθε συνάρτηση ξεκινάει με δικό της σωρό. Ότι αφήσω στο σωρό στην επιστροφή από τη συνάρτηση θα διαγραφεί. Η Σειρά βάζει τα νούμερα στο τέλος του σωρού, η βάλε στην αρχή. Έτσι η Σειρά 5,4 θα βάλει τελευταίο στοιχείο το 4, ενώ η Βάλε 7, 8 θα αφήσει στη κορυφή (ή πρώτο στοιχείο) το 8. Με την Διάβασε διαβάζω από το σωρό. Όταν καλούμε μια συνάρτηση περνάμε όσες παραμέτρους θέλουμε. Δεν μετράει η Μ2000 τι και πόσα βάζουμε. Ότι δώσουμε τα αφήνει στο σωρό τιμών. Το πρόγραμμα που γράφουμε αναλαμβάνει τι να κάνει με το σωρό. Αν βάλουμε τη Διάβασε Χ απλά του λέμε πάρε την τιμή από την κορυφή και βάλς την στην Χ αν υπάρχει αλλιώς φτιάξε την πρώτα και μετά βάλε την!
Η Διάβασε δημιουργεί μεταβλητές αλλά όχι πίνακες. Μπορούμε να ορίσουμε έναν μηδενικό πίνακα με την Πίνακας Α() και να διαβάσουμε από το σωρό τον πίνακα που έχει αφήσει κάτι άλλο, με μια Διάβασε Α(). Η Βάλε Α() θα βάλει αντίγραφο του πίνακα στο σωρό. Ο πίνακας στο σωρό κρατάει μια μόνο θέση. Αν δώσουμε Πίνακας Β() και αργότερα Β()=Α() τότε παίρνουμε αντίγραφο του Α() στο Β(). Αν μέσα στο Α() υπάρχουν αντικείμενα (ομάδες) τότε θα πάρουμε αντίγραφα των ομάδων, και αυτά έχουν πίνακες και αντίγραφα των πινάκων...και αν αυτά έχουν αντικείμενα...αντίγραφα των αντικειμένων. Δεν υπάρχει περίπτωση κυκλικής αναφοράς! Δηλαδή να χρειαστεί να αντιγραφεί κάτι που έχει ήδη αντιγραφεί!  Και αυτό γιατί η Μ2000 δεν αντιγράφει δείκτες προς αντικείμενα (ομάδες λέγονται στην Μ2000) αλλά τα αντικείμενα! αν Α είναι μια ομάδα τότε το Β=Α δίνει μια νέα ομάδα αν το Β δεν είναι ομάδα  ή μια ένωση (αντιγράφει τιμές στις ίδιες ιδιότητες και προσθέτει εκείνες που δεν υπάρχουν στην Β). Μπορούμε να χρησιμοποιήσουμε αναφορές σε ομάδες, αλλά αυτές γίνονται σε ανοιχτές ομάδες, ενώ ότι είναι μέσα σε στοιχειο πίνακα ή στο σωρό είναι κλειστή ομάδα, ανώνυμη. Μια κλειστή ομάδα μπορεί να ανοίξει πρόσκαιρα και να περαστεί με αναφορά, όμως αυτό δεν εξασφαλίζει ότι κάπου αλλού ταυτόχρονα θα ανοίξει πάλι η ομάδα. Απλά στο δεύτερο άνοιγμα θα υπάρχει η αρχική κλειστή ομάδα, και τυχόν αλλαγές ενώ θα αποθηκευτούν μια φορά, θα αποθηκευτούν στο κλείσιμο και μια δεύτερη. Αν αυτά τα κάνουμε σε νήματα...απλά θα έχουμε απροσδιόριστη συμπεριφορά. Υπάρχει τρόπος όμως να εκτελείται ένα μέρος κώδικα χωρίς να επιτρέπεται άλλο μέρος με ην ίδια μεταβλητή χειρισμού να τρέξει αν πρώτα δεν τελειώσει αυτός που
έχει το "πάνω χέρι" στη μεταβλητή! Αυτή η εντολή λέγεται μέρος { }
Εδώ θα δούμε μόνο το οκ 2 και Αληθές. Η δομή μέρος { } ως μεταβλητή_χειρισμού αλλάζει την μεταβλητή σε αληθές, οπότε τα άλλα νήματα που τρέχουν στο ίδιο τμήμα και βλέπουν τις ίδιες μεταβλητές θα προσπεράσουν αυτό το κομμάτι! (αν έχουμε σχέδιο.νημάτων ταυτόχρονο και όχι διαδοχικό) μπορούμε να κάνουμε την Κ γενική (Γενική Κ=Αληθές)..και να έχουμε και σε κλήσεις μια παρόμοια συμπεριφορά! Π.χ να αποκλείται μια συγκεκριμένη λειτουργία αν έχει κληθεί μια συνάρτηση μέσα από μια μέρος { } ή αν έχουμε σε δικό μας χειρισμό την μεταβλητή (η μεταβητή είναι μια απλή μεταβλητή, που την αναγνωρίζει η Μέρος { } και στο τέλος του μπλοκ την κάνει Ψευδές).

Κ=Αληθές
μέρος {
      Τύπωσε "οκ 1", Κ
} ως Κ
Κ=Ψευδές
μέρος {
      Τύπωσε "οκ 2", Κ
} ως Κ


Σε αυτό το πρόγραμμα βλέπουμε ότι μετά το 74 στοιχείο οι δυο ρουτίνες έχουν μικρές διαφορές λόγω στρογγυλοποίησης. Κάνουμε προσθέσεις αλλά οι μεγάλοι αριθμοί στρογγυλοποιούνται άρα πρακτικά δεν παίρνουμε πραγματικό αριθμό Φιμπονάτσι αλλά κάποιον κοντινό!




Οθόνη 5,0
Περιθώριο {Οθόνη 5}
Πένα 14
Διαστιχο 0
Φόρμα 60 \\ δεν δίνουμε γραμμές για να βάλει όσες μπορεί!
\\ Κάνουμε δυο μεταβλητές γενικές για να τις διαβάζει η συνάρτηση παρακάτω
\\ Η εντολή Κάνε φτιάχνει μια συνάρτηση σε μια γραμμή αντί να την γράφαμε έτσι:
\\ Συνάρτηση Ακ.Φιμπ { Διάβασε χ : =ακ(απολ((Συζυγής_ΧΑ^χ-(-Συζυγής_ΧΑ^(-χ)))/ρ5)) }
Γενική ρ5=ριζα(5)
Γενική Συζυγής_ΧΑ=1/((1+ρ5)/2)
\\ αυτή είναι μια
Κάνε Ακ.Φιμπ(χ)=ακ(απολ(Συζυγής_ΧΑ^χ-(-Συζυγής_ΧΑ^(-χ)))/ρ5)
Συνάρτηση Φιμπ {
     Διάβασε Χ
     Σειρά 1,1
     Ενώ Χ>2 {
           Διαβασε Β, Α
           Βάλε Β, Β+Α
           Χ--
     }
      =Αριθμός
}
Χρυσή_Τομή=1/Συζυγής_ΧΑ
Τύπωσε Συζυγής_ΧΑ, Χρυσή_Τομή
Για ι=1 εως 100 {
      \\ η τύπωσε μέρος χειρίζεται στήλες με μεταβλητό πλάτος
      \\ εδώ η πρώτη έχει 6 χαρακτήρες και ακολουθούν οι άλλες με 24 χαρακτήρες
      \\ ο αριθμός 0 στην πρώτη $( ) δηλώνει ότι θα γραφει η Τύπωσε μη αναλογική γραφή
      \\ εξ ορισμού το νούμερο είναι 4 (με αναλογική γραφή) στις Τύπωσε μέρος/πάνω/υπό
      Τύπωσε μέρος $(0,6),ι, $(, 24), Ακ.Φιμπ(ι), Φιμπ(ι)
      \\ υποχρεωτικά μετά την Τύπωσε μέρος χρειάζεται αλλαγή γραμμής με μια απλή τύπωσε
      \\ διαφορετικά δεν αλλάζει γραμμή και πέφτουν όλα πάνω σε μια (χωρίς να σβήνουν τα προηγούμενα).
      Τύπωσε
}


Κυριακή, 24 Ιανουαρίου 2016

Αναθεώρηση 142 - Αλλαγή μεγέθους Stack

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

Όταν η γλώσσα ανοίγει στο IDE της Vb6 και τρέχει εκεί τότε αυτόματα οι κλήσεις περιορίζονται σε 128. Οι ρουτίνες (Subs) έχουν αρχικό όριο 10000 αλλά μπορούμε να το αλλάξουμε, π.χ. 50000 γιατί δεν χρησιμοποιείται stack όπως στις ρουτίνες, αλλά μνήμη γενικότερα. Ως προς τις ρουτίνες δηλαδή δεν άλλαξε κάτι. Υπενθυμίζω ότι μπορούμε να καλέσουμε μια ρουτίνα χωρίς την εντολή ΔΙΑΜΕΣΟΥ αν δεν έχουμε πίνακα με το ίδιο αναγνωριστικό. Οι ρουτίνες και οι συναρτήσεις έχουν αναδρομή, δηλαδή μπορούν να καλέσουν τον εαυτό τους. Οι ρουτίνες δεν έχουν αντικείμενο εκτέλεσης, αλλά τρέχουν στο αντικείμενο που της καλεί, σε τμήμα ή συνάρτηση..Είναι πιο ελαφριές αλλά δεν μπαίνουν όπως οι συναρτήσεις μέσα παραστάσεις, αριθμητικές ή αλφαριθμητικές. Όμως χρησιμοποιούν των σωρό τιμών και μπορούν να αφήσουν εκεί ότι θέλουμε, ή να χρησιμοποιήσουμε μεταβλητή με αναφορά, ή να χρησιμοποιήσουμε μια μεταβλητή του τμήματος ή συνάρτησης που το κάλεσε, αφού μια ρουτίνα έχει πρόσβαση σε ότι βλέπει το τμήμα/συνάρτηση που την καλεί.


Αν αλλάξουμε το κώδικα της Μ2000 (π.χ. θέλουμε να προσθέσουμε κάποιες εντολές), και φτιάξουμε το exe αρχείο (με την Vb 6) , τότε πρέπει να εκτελέσουμε το παρακάτω πρόγραμμα, το οποίο το γράφουμε σε ένα αρχείο stack.gsb  (υπάρχει ένα παρόμοιο στο φάκελο με τα αρχεία του προγράμματος, αλλά αυτό εδώ είναι πιο γενικό)
Χρειαζόμαστε το Editbin το οποίο το βρίσκουμε ή στο φάκελο της VC98, ή κατεβάζουμε το masm32, από το Masm32 SDK

Στο Module A έχουμε το τεστ με τη μεγάλη αναδρομική κλήση. Στο Module B έχουμε το πρόγραμμα που φτιάχνουμε δυο bat αρχεία (batch τα λέμε), όπου ένα καλούμε για να να δώσει πληροφορίες και το άλλο για να κάνει τη δουλειά, να αλλάξει το stack. Ο λόγος που το έσπασα στα δύο είναι γιατί πρέπει να κλείσει η Μ2000 και μετά να κάνουμε την αλλαγή.  Το Set End στέλνει στον μεταφραστή γραμμής την εντολή End, ενώ χωρίς το Set το End κάνει ότι και η Exit - έξοδος από το μπλοκ (μπλοκ θεωρείται και το τμήμα, αλλά δείτε στο πρόγραμμα, έχω χρησιμοποιήσει την Break για να βγω από το τμήμα, μέσα από άλλο μπλοκ μέσα στο τμήμα). Η Rem ή Σημ κάνει ότι και η \ ή ' δηλαδή αφήνει τη γραμμή ως σημειώσεις (remarks) αλλά επιτρέπει στον διορθωτή να χρωματίσει σαν να ήταν οι σημειώσει εντολές.  Δείτε εδώ ότι γράφουμε στο cmd παράθυρο το Stack (το ίδιο όνομα με το gsb, αλλά εδώ καλούμε το Stack.bat)

Δείτε με τη βοήθεια τις εντολές Dos και Win
π.χ. Win dir$ ανοίγει τον τρέχον φάκελο στον explorer. H Win είναι σαν Start, π.χ. Win Calc ανοίγει την αριθμομηχανή. Μπορεί όμως να ανοίξει έγγραφα (δηλαδή τύπους αρχείων που γνωρίζει το σύστημα με τι ανοίγουν). Η εντολή Dos στέλνει στο cmd ότι θέλουμε (σε μια γραμμή).
Αυτές οι εντολές ΔΕΝ λειτουργούν αν επιλέξουμε χρήστη (δηλαδή όχι τον αρχικό αλλά κάποιο όνομα με την εντολή User ή Χρήστης). Τότε η Μ2000 περιορίζει την πρόσβαση για ασφάλεια, στο φάκελο του χρήστη. 
Ακόμα και αν αλλάξουμε τον τρέχοντα φάκελο..μπορούμε να επιστρέψουμε στο φάκελο χρήση με την εντολή Dir User  ή Κατάλογος Χρήστη. Η εντολή Dir ή Κατάλογος χωρίς παράμετρο μας δίνει τα περιεχόμενα του καταλόγου. 



Module A {
      Function rec {
                  Read bb
                  If bb<=0 Then =0 : Exit
                  Print bb
                  =rec(bb-1)
      }
      a= rec(14500)
}
Module B {\\ script For making needed batch files For setting stack For m2000.exe
      \\ when we make the final exe
      curdir$=Dir$
      \\ use editbin from masm32
      rem defaultpath$="C:\masm32\bin\"
      \\ or use the one from VC98
      defaultpath$="C:\Program Files\Microsoft Visual Studio\VC98\Bin\"
      If Not Exist(defaultpath$+"editbin.exe") Then {
            Dir ? "c:", "Need Editbin.exe Folder"
            defaultpath$=Dir$
            Dir curdir$
            If Not Exist(defaultpath$+"editbin.exe") Then Break
      }
      document dd$= {Echo off
            Cls
            Echo Set Stack Size For M2000 - press enter
            }
      \\ = is append For document  - use clear dd$ to clear it.
      dd$ = Shortdir$(defaultpath$) + {editbin /stack:102402048 m2000.exe}
      Dir Appdir$
      \\ without "For Wide Output" we output ascii text
      Open "stack.bat" For Output As i
      Print #i, dd$
      Close #i
      Clear dd$
      dd$ ={Cls
            Echo Execute stack.bat to set stack size For m2000, ver 8, rev 142+
            }
      Open "Info.bat" For Output As i
      Print #i,"Echo off"
      Print #i, "cd "+Quote$(Appdir$)
      Print #i, dd$
      Close #i
      Dos "cd "+Quote$(Appdir$) + " &&  Info.bat"
      Set End
}

Υλοποιώ την 8.1 έκδοση. Θέλει δουλειά, αλλά είναι σε ένα 70%. Το ζήτημα εδώ είναι να γίνει η αναζήτηση μεταβλητών στον ίδιο χρόνο ανεξάρτητα από το πόσες φτιάχνει κανείς. Στην υπάρχουσα υλοποίηση (έκδοση 8, αναθεώρηση 142), οι μεταβλητές αναζητούνται σειριακά από την τελευταία. Η τελευταία είναι και η τελευταία που δημιουργήθηκε. Επιπλέον στη Μ2000 δεν υπάρχει αναζήτηση πέρα του τμήματος, παρά μόνο για γενικές. Οπότε στις γενικές υπάρχει ένα πέναλτι χρόνου, αν όμως βρίσκονται στο βάθος, δηλαδή αν έχουμε μεγάλη διαφορά "απόστασης". Πραγματικά η απόσταση εδώ έχει νόημα αφού τα ονόματα γράφονται σε ένα αλφαριθμητικό. 
Η έκδοση 8.1 χρησιμοποιεί πίνακα κατακερματισμού που δύναται να επεκταθεί. Στην επέκταση χρησιμοποιούνται μερικά αποτελέσματα για να υπολογιστούν οι νέες θέσεις (δηλαδή αν έχουμε 1000 μεταβλητές θα γίνουν χίλιοι υπολογισμοί και θα περαστούν σε θέσεις στον πίνακα κατακερματισμού και όπου υπάρχουν συγκρούσεις θα ενημερωθεί ένας δείκτης "επόμενο"). Η λογική του πίνακα κατακερματισμού έχει εμφανιστεί εδώ στο blog σαν πρόγραμμα της Μ2000.

Κερδίζουμε από τη χρήση του πίνακα γιατί για κάθε φορά που αναζητούμε κάτι εκτελούμε την συνάρτηση κατακερματισμού και μας λέει σε ποια θέση στο πίνακα πέφτει αυτό που ζητάμε. Κοιτάμε αν υπάρχει εκεί και αν όχι τότε κοιτάμε τι δείχνει αυτό..ως επόμενο...μέχρι να το βρούμε ή να μην υπάρχει επόμενο. Ουσιαστικά πάμε σε σειριακή αναζήτηση μόνο σε εκείνα τα στοιχεία που δίνουν ίδιο αποτέλεσμα στην συνάρτηση κατακερματισμού. Πετυχαίνουμε εν τέλει σχεδόν σταθερό χρόνο αναζήτησης για οποιοδήποτε αριθμό στοιχείων! Πιο γρήγορος τρόπος υπάρχει με collection (την παρέχει η vb) αλλά δεν ταιριάζει με την περίπτωση εδώ, επειδή στη Μ2000 μπορούμε να ορίσουμε τοπική μεταβλητή με ακριβώς ίδια στοιχεία, για το ίδιο αντικείμενο εκτέλεσης, άρα ο πίνακας κατακερματισμού θα έχει ίδια κλειδιά και το collection της Vb6..δεν το δέχεται. Εδώ στα ίδια κλειδιά διαβάζουμε πάντα από το τελευταίο.

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

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







Παρασκευή, 22 Ιανουαρίου 2016

Benchmark σε Windows 8 (VirtualBox)

Από την έκδοση 157 μπορούμε να χρησιμοποιούμε το πρόγραμμα m2000.exe χωρίς να δηλώσουμε τα ελληνικά ως γλώσσα για το κείμενο σε προγράμματα που δεν υποστηρίζουν Unicode.
H M2000 υποστηρίζει Unicode (και έγινε και μια τελική διόρθωση στο 157)
 
Χρησιμοποιώντας κώδικα από εδώ για ελληνικά  και από εδώ για αγγλικά με την έκδοση 8 αναθεώρηση 141, σε Windows 8 - 64bit σε Virtual Box (στο ίδιο μηχάνημα που δοκιμάζω και σε Windows 7 32bit). Η ταχύτητα είναι στο νορμάλ (Γρήγορο - Fast). Δείτε εδώ Πώς κάνουμε πιο γρήγορο το πρόγραμμα με μια εντολή..και πώς πιο αργό;  (Fast !, Fast, Slow ή Γρήγορα !, Γρήγορα, Αργα)

Συμπεράσματα: Στην έκδοση 141, η ταχύτητα και των δυο γλωσσών είναι σχεδόν ίδια. Και στις δυο περιπτώσεις τα ονόματα των μεταβλητών γυρνούν σε κεφαλαία κάθε φορά που τα βρίσκει ο διερμηνευτής! Αυτό συμβαίνει γιατί αποθηκεύονται με κεφαλαία γράμματα, και στα ελληνικά αφαιρούνται οι τόνοι. 
Ένα ερώτημα εδώ θα ήταν: "Γιατί οι μεταβλητές δεν υπολογίζονται από πριν;" 
Η απάντηση δεν είναι απλή. Αν γίνονταν αυτό τότε οι μεταβλητές που θα είχαμε θα ήταν υπολογισμένες από πριν για δεδομένο μπλοκ, άρα δεν θα μπορούσαμε να είχαμε αντικείμενα τα οποία θα είχαν δικές τους μεταβλητές, και θα έπρεπε να δηλώναμε τι ακριβώς θα έχουν. Άρα άλλαζε το σκεπτικό της Μ2000 που δεν θέλει να περιορίζει το τι ιδιότητες και μεθόδους έχει ένα αντικείμενο. Το αντικείμενο η Μ2000 το βλέπει ως κάτι που εξελίσσεται στην εκτέλεση, και όχι κάτι σταθερό, κάτι μόνιμο, όπως το θεωρούν άλλες γλώσσες. 

Δείτε στο παράδειγμα πως προσθέτουμε ιδιότητες σε αντικείμενα!

Class thing {
      day=1
}
Dim a(10)=thing()
PushOne(0,"night=2")
PrintOne(0,"night")
PushOne(0,{Alfa$="George"})
PrintOne(0,"Alfa$")
PrintOne(0,"day")
a(3)=a(0) ' copy a(0) to a(3)
PrintOne(3,"Alfa$") ' George
PushOne(3,{Alfa$="Peter"}) ' Peter
PrintOne(3,"KLM") ' not exist

Sub PrintOne(item, a$)
      Try Ok {
            inline "Print a("+str$(item)+")."+a$
      }
      If not Ok then Print "not exist"
End sub
Sub PushOne(item, a$)
Print "item, a$", item, a$
For a(item) {
      inline "group tmp {"+a$+"}"
      This=tmp 'union
}
End Sub




Benchmark (τιμές σε χιλιοστά του δευτερολέπτου)



Τετάρτη, 20 Ιανουαρίου 2016

Μικρή αναθεώρηση 141

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

Κυριακή, 17 Ιανουαρίου 2016

Στατικές Μεταβλητές (Νέο) και Νήματα

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

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

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

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

Στα αγγλικά ένα νήμα είχε μέχρι τη 139 αναθεώρηση μια εντολή δημιουργίας THREAD { } AS και τέσσερις επιμέρους, όπως INTERVAL, HOLD, ERASE, RESTART. Τώρα έχει και την THREAD SET για μια σειρά στατικών μεταβλητών και την EXECUTE με την οποία από άλλο νήμα μπορούμε να εκτελέσουμε μια εντολή άμεσα στο νήμα που θέλουμε!

Παρακάτω είναι δυο προγράμματα στα αγγλικά (στα προχωρημένα θέματα προτιμώ τα αγγλικά, γιατί έχουν παραπονεθεί παλιοί προγραμματιστές για τα Ελληνικά).

(επιπλέον αλλαγές που έχει η 140 είναι ένα 5% βελτίωση στη ταχύτητα)

Στο πρώτο πρόγραμμα με space bar ρίχνουμε τρία νούμερα στο νήμα. Με πάτημα πλήκτρου ποντικιού τερματίζει το πρόγραμμα. Φαίνονται πολλά στοιχεία, όπως η χρήση ρουτίνας, τα δυο αυτόματα νήματα, το AFTER { } και το MAIN.TASK { }.


\\ Example of using static variable in Thread
\\ and new command "Execute" for inject commands..to Thread
\\ Here we place data in Thread's stack for values
\\ and also we place a specific assignment (ok=I>2000)
\\ we can mix plans
Thread.plan Sequential

OK=False : I=10
After 1000 {
      Print "Hello There - this is one time running Thread"
}
\\ but not this....
Thread {
      If Not Empty Then Read mmm : Print ">>>>>>>>>>>", mmm
      Thread set I=100 ' this set once If no I found as static in Thread
      printme(I)
      I++
} as k interval 10


\\ this is a Thread too (we can use non Thread like Every 80 { ]


Main.Task 80 {
      If mouse=1 Then Exit
      Print "wait", I ' this is module's I
      If Inkey$=" " Then {
            \\ Data command do a merge in the bottom of thread stack
            \\ so is  FIFO mechanism
            \\ Use Push command to make it LIFO
             Thread K Execute Data I**2, I**1.5, I**.5
      }
      ' here Execute run on K Thread so I is a static variable in K Thread
      ' but OK is module's variable
      Thread K Execute OK=I>2000
      If OK Then Thread k Erase : Exit
}
\\ for all conditions is better to Erase all threads
\\ but Main.Task (is a Thread also) erase modules threads
Threads Erase
\\ when interpreter found Sub stop normal the prorgam
Sub printme(X)
      Print X
End Sub



Το δεύτερο πρόγραμμα φτιάχνει πέντε πανομοιότυπα νήματα, τα οποία καλούν ένα τμήμα όπου αρχικά τροφοδοτούν δυο στατικές μεταβλητές του. Κάθε νήμα θα δώσει άλλα στοιχεία, και εν τέλει το τμήμα φαίνεται να "υπάρχει" πέντε φορές! Έχω χρησιμοποιήσει σχέδιο νημάτων ταυτόχρονο (έτσι το λέω στη Μ2000, στο προηγούμενο παράδειγμα έχω χρησιμοποιήσει το διαδοχικό, δηλαδή τερματίζει το κάθε νήμα και μετά ξεκινάει το άλλο - σε χρονοβόρες διαδικασίες όπως π.χ. να περιμένουμε εισαγωγή χαρακτήρων σε ένα νήμα...συνεχίζουν τα άλλα νήματα να τρέχουν)
Εδώ το If  Then Else δεν μπορεί να "πετύχει" στο σπάσιμο και έτσι το έχουμε σε μπλοκ, επειδή τα μπλοκ έχει εντολές που θα τρέξουν μαζί όπως και να έχει το πράγμα!
Η once είναι στατική για κάθε νήμα. Και σε κάθε νήμα θα τρέξει η look με άλλες στατικές!
Στην Look η εντολή Static θα δει αν υπάρχουν οι μεταβλητές. Αν υπάρχουν δεν εκτελεί το =Number (το οποίο διαβάζει από σωρό τιμών αυτά που βάζει το νήμα).
Μετά το Thread {  } As variable μπορούν να μπουν μια από τις δύο Interval ή Execute. Αν δεν μπει το Interval (έστω και μετά) δεν ξεκινάει το νήμα.

Σε αργούς υπολογιστές το  concurrent (ταυτόχρονο) εκτελείται με περίεργο τρόπο (εκτελεί τα πέντε νήματα και σαν να ξεκουράζεται, και μετά συνεχίζει...). Δοκιμάστε και με τα δύο συστήματα, βγάλτε το rem από το ένα και βάλτε το στο άλλο trhead.plan (Σχέδιο.νημάτων)



rem thread.plan sequential
thread.plan concurrent \\ in concurrent plan each thread run one command and switch to other
\\ with respect to time interval (each thread run in a minimum interval)
\\ but this can be extent.
\\ All threads need time to run
\\ here we choose only to set two wait commands
module look {
      ' static set once
      ' each thread call module look
      ' from own process, so we have 5 set of static variables
      Static L=number, b=number
      L++
      Print format$( "look {0} from {1}", L,b)
}
For I=1 to 5 ' just change 5 to 10 and we get 10 threads
      thread { ' set one time      
      thread set once=true \\ if then else in a block because we choose concurrent plan}
      {if once then { look klm, bb : once=false } else look}
      Print m, bb, b$, klm
      m++
      } as b execute thread set m=10**i, bb=b, b$="George", klm=random(1000)
      Thread b interval 100 ' set interval to 30 to see how fast screen change     
Next I
Wait 2000
thread b execute b$ = "Hello There " +b$
thread b-3 execute b$ = "Good Bye " +b$
Wait 2000
list   ' only I and B are modules variables
threads erase

Παρασκευή, 15 Ιανουαρίου 2016

Αναθεώρηση 139, εμπλουτισμός!

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


\\ η απλή ρουτίνα παραμένει ως έχει
\\ η απλή δεν έχει παράμετρους
\\ και δεν έχει "προσωρινό χώρο δημιουργίας"
Διαμέσου απλήρουτίνα
\\ νέος τρόπος για ρουτίνες - όπου ότι δημιουργούμε σβήνεται
Τύπωσε(10,34,50)
\\ παλιός
Διαμέσου Τύπωσε("Γεια Χαρά", 10)
Α=1
Παραγοντικό(100, &Α)
Τύπωσε(Α)


Ρουτίνα Τύπωσε()
πάλι:
      Αν Κενό Τότε {
            Τύπωσε
            Έξοδος Ρουτίνας
      } Αλλιώς.Αν ΕινΑρ Τότε {
            Τύπωσε Αριθμός,
      } Αλλιώς {
            Τύπωσε Γράμμα$,
      }
      Προς πάλι
Τελος Ρουτίνας
Ρουτίνα Παραγοντικό(Χ, &αποτ)
       Αν Χ<=1 τότε Έξοδος Ρουτίνας
       Παραγοντικό(Χ-1, &αποτ)
       Αποτ*=Χ
Τέλος Ρουτίνας
απλήρουτίνα:
      Τύπωσε("οκ")
Επιστροφή



3. Διόρθωση στο χρωματισμό στην Όρισε (το κείμενο στις αγκύλες ήταν σαν αλφαριθμητικό)

Όρισε Μήνυμα Από "user32.MessageBoxW" {Μακρυς Α, κειμενο$, κειμενο2$, Μακρυς τυπος}
Τυπωσε Μήνυμα(Παραθυρο, "Γεια χαρά", "Γιώργος", 2)

Συνδεδεμένες Λίστες FIFO και LIFO για αντικείμενα!


Το παράδειγμα είναι αρκετά προχωρημένο και είναι στα Αγγλικά. Η κλάση Mem() που είχε δοθεί σε προηγούμενες αναρτήσεις εμπλουτίστηκε με "λογική" για υποστήριξη συνδεδεμένων λιστών τύπου LIFO και FIFO. Μπορούμε να φτιάξουμε όσες λίστες θέλουμε! Η κλάση Mem() αν χρειάζεται περισσότερα στοιχεία επεκτείνεται μόνη της! Μας παρέχει μνήμη για να βάλουμε αντικείμενα σε ένα πίνακα.
Στη Μ2000 μπορούμε σε στοιχεία πίνακα να γράφουμε αντικείμενα. Αντί να χρησιμοποιήσουμε τη συνάρτηση Malloc() που δίνει το αντικείμενο για να κάνουμε λίστες χρησιμοποιούμε τις μεθόδους PushFirst και PushLast, οι οποίες προσθέτουν μια ιδιότητα την pnext.
Η Mem() δεν ασχολείται με το τι κλάση δίνουμε! Αν χρησιμοποιούμε τις μεθόδους αυτές, για οποιοδήποτε αντικείμενο, τότε θα φτιάχνεται κανονικά η λίστα που θέλουμε! Οι δείκτες αρχής-τέλους για την FIFO και αρχής για τη LIFO είναι μεταβλητές που παρέχουμε με αναφορά.
Επιπλέον υπάρχουν και μέθοδοι και συναρτήσεις υποστηρικτικές. Δεν θα αναφέρω κάτι παραπάνω!
Μπορεί κανείς να δει το κώδικα - αλλά μπορεί και να τον τρέξει!
Η Μ2000 έχει σωρό τιμών που χειρίζεται ως στοίβα FIFO και LIFO, ότι βάλουμε,  αλλά εδώ μας ενδιαφέρει η χρήση μια κλάσης. Όλο το MEM()  είναι ένα αντικείμενο και μπορεί να μπει σε ένα άλλο!

Διορθώθηκε ο παλιός κώδικας (έβαζε 100 στοιχεία κάθε φορά που ζητάγαμε 1, αλλαγή συνθήκης < σε >= στο .topfree>=.Items, στην Malloc)
Προστέθηκε η PopOne για να βγάζουμε στοιχεία. Επίσης σε αυτήν την έκδοση δεν γίνεται χρήση της .Porperty$() της Mem, αλλά έχει εισαχθεί μια ιδιότητα Item() (με ένα όρισμα) η οποία γυρνάει αντίγραφο του αντικειμένου και συνδυάζεται με .name$ (αυτό γίνεται στην 9). Για το διάβασμα η Item() διαβάζει το αντικείμενο και το συνδέει. Πρέπει όμως να υπάρχει ήδη αντικείμενο εκεί που το βάζουμε!




\\ Example of using Mem() to build \\ Language M2000 ver 9 \\ use of public and private in class/group definitions \\ Stacks, FIFO and LIFO \\ We provide a class AnyName wihout pnext \\ and inside Mem() we add This property \\ We use PushFirst and PushLast methods Form 80,40 Gosub MyClass Class AnyName {       name$       Module AnyName {             Read .name$       } } A=Mem(100) Print "A FIFO, First In First Out" A1=A.Null() A2=A.Null() Stack New {       Data "First","Second","Third", "Forth","Fifth"       While Not Empty {             A.PushFirst &A1,&A2, AnyName(letter$)       }       ShowList(A1) } Print "A LIFO, Last In First Out" A3=A.Null() Stack New {       Data "First","Second","Third", "Forth","Fifth"       While Not Empty {             A.PushLast &A3, AnyName(letter$)       }       ShowList(A3) } Print "Change one item ********************" bb=A.CopyObj(A3) Print bb.name$ bb.name$=bb.name$+"!" A.CopyBack A3, BB \\Print A.Property$(A3,"name$") Print A.Item(A3).name$ Print "Swap 1st with 3rd using external copies***" x=A3 : For i=1 to 2 : x=A.Pnext(x) : Next i Dim aa(2) aa(0)=A.CopyObj(A3) aa(1)=A.CopyObj(x) Swap aa(0),aa(1) 'fast using pointers \\ CopyBack preserve link pointer if found A.CopyBack A3, aa(0) A.CopyBack x, aa(1) Dim aa() ' erase Print "Show List - Swap 1 with 3rd **" ShowList(A3) \\ using object method A.SwapLinked A3, x Print "Show List - Right Order ***" ShowList(A3) Print "Show List - Delete Second from Last***" A.DeleteLinked A3 '' delete second ShowList(A3) Print "Show List - Delete Last ***" For A {       \\ newA3 is a temporary variable       newA3=.Pnext(A3)       .MFree A3       A3=newA3 } ShowList(A3) OldA3=A3 OldA1=A1 Dim bbb(1) bbb(0)=A Print bbb(0).ItemsCount For bbb(0) {       Print "A1"       x=A1       While x<>.Null() {             Print .Item(x).name$             x=.Pnext(x)       }       Print "A3"       x=A3       While x<>.Null() {             Print .Item(x).name$             x=.Pnext(x)       }       Print "Pop from A3"       M=.PopOne(&A3)       Print M.name$       Print .itemscount       M=.PopOne(&A3)       Print M.name$       Print .itemscount       Print "A3"       x=A3       While x<>.Null() {             Print .Item(x).name$             x=.Pnext(x)       }       Print "Pop from A1"       M=.PopOne(&A1)       Print M.name$       Print .itemscount       M=.PopOne(&A1)       Print M.name$       Print .itemscount       Print "A1"       x=A1       While x<>.Null() {             Print .Item(x).name$             x=.Pnext(x)       } } \\ bbb(0) has a copy of A, so we have in A all 8 items, as was before copy A to bbb(0) Print "Original A1" ShowList(OldA1) Print A.Item(OldA1).name$ For This {       M=A.Item(OldA1)       M.name$="new name"       A.Item(OldA1)=M } Print A.Item(OldA1).name$ Print "Original A1 with changed name in A1" ShowList(OldA1) Print "Original A3" ShowList(OldA3) End Sub ShowList(Root)       local x=Root       While x<>A.Null() {             Print A.Item(x).name$             x=A.Pnext(x)       }       Print "Press any key"       a$=key$ End Sub MyClass: Class Global Mem { Private:       Dim d()       noUsed=-1       topfree=0       Items, Count, AddMore=100       Group Null { Null }       Function IsNull {             =Valid(.d(Number).Null)       } Public:       Group Item {             value (x) {                   Link parent d() to d()                   IF valid(d(x).Null) then Error "Null at"+Str$(x)                   IF Type$(d(x))<>"Group" then Error "Empty at"+Str$(x)                   =d(x)             }             Set (x) {                   Link parent d() to d()                   IF valid(d(x).Null) then Error "Null at"+Str$(x)                   IF Type$(d(x))<>"Group" then Error "Empty at"+Str$(x)                   b=d(x).pnext                   Read a                   Group a {                         pnext=b                   }                   d(x)=a             }       }       Group ItemsCount {             Value {                   link parent count to count                   =count             }       }       Function Pnext {             Read where             if .isNull(where) Then "Error pointer is Null"             =.d(where).pnext       }       Module PushLast {       If not Match("SG") Then Error "Bad Parameters"             Read &First             Push 1 ' For malloc             Shift 2 ' so we get Group first             \\ we call Malloc module using parameters in stack             Call .Malloc             Read Last             If First=.Null() Else {                   .d(Last).pnext<=First             }             First=Last       }       Module PushFirst {             If not Match("SSG") Then Error "Bad Parameters"             Read &Root, &Last             Push 1 ' For malloc             Shift 2 ' so we get Group first             \\ we call Malloc module using parameters in stack             Call .Malloc             Read First             If Root=.Null() Then {                         Root=First                         Last=First                   } Else {                         .d(Last).pnext<=First                         Last=First                   }       }       Function Null {             =-1       }       Module AddMore {             Read K             If K<=0 Then Exit             Try ok {             Dim .d(K+.items)             }             If ok Then .Items+=K       }       Module Mem {             Read .Items             If Match("G") Then Read N             N=.Null \\ This is a Union If N is a Group             Dim .d(.Items)=N       }       Function Malloc {             If Not Match("G") Then Error "No Group Found"             Call .Malloc             =Number       }       Module Malloc {             If .noUsed=-1 Then {                   If .topfree>=.Items Then {                         Try {                             .AddMore .AddMore                             ok2=true                         }                         If valid(ok2) Else Error "Memory Full"                   }                   Read .d(.topfree)                   Gosub CheckCommand(.topfree)                   Push .topfree                   .topfree++                   .count++             } Else {                   temp=.d(.noUsed).Null                   Read .d(.noUsed)                   Gosub CheckCommand(.noUsed)                   Push .noUsed                   .noUsed<=temp                   .count++             }                    Sub CheckCommand(X)                   If Match("N") Then {                         If Number=1 Then { \\ use Interface                               For .d(X) {                                     Group kk { pNext=-1}                                     This=kk                               }                         }                   }             End Sub       }       Module Mfree {             Read mf             If .IsNull(mf) Then Error "Invalid Pointer"             old=.noUsed            .noUsed<=mf            .d(mf)=.Null            .d(mf).Null<=old            .count--       }       Function Property {             Read A, A$             A$=".d(A)."+A$ ' M2000 has string pointers             If .IsNull(A) Then Error "Invalid Pointer "+str$(A)             =Eval(A$)             If Match("N") Then A$.=Number       }       Function Property$ {             Read A, A$             A$=".d(A)."+A$             If .IsNull(A) Then Error "Invalid Pointer"             =Eval$(A$.) ' look . after A$             \\ A$. is Not A$ is a pointer To             If Match("S") Then A$. = letter$       }       Function CopyObj {             Read A             If .IsNull(A) Then Error "Invalid Pointer"             =.d(A)       }       Module CopyBack {             Read A             If .IsNull(A) Then Error "Invalid Pointer"             If valid(.d(A).pnext) Then {                   k=.d(A).pnext                   Read .d(A)                   If valid(.d(A).pnext) Else {                         For .d(A) {                               Group kk { pnext }                               This=kk                         }                   }                   .d(A).pnext<=k             } Else {                   Read .d(A)             }       }       Module SwapFast {       Read A, B             If .IsNull(A) Then Error "Invalid Pointer A"             If .IsNull(B) Then Error "Invalid Pointer B"             Swap .d(A), .d(B)       }       Module SwapLinked {             Read A, B             \\ no error checking if we don't have linked list             If .IsNull(A) Then Error "Invalid Pointer A"             If .IsNull(B) Then Error "Invalid Pointer B"             ka=.d(A).pnext : kb=.d(B).pnext             Swap .d(A), .d(B)             .d(A).pnext<=ka : .d(B).pnext<=kb       }       Module DeleteLinked {             Read A             \\ no error checking if we don't have linked list             If .IsNull(A) Then Error "Invalid Pointer"             B=.d(A).pnext             If .IsNull(B) Then Exit 'deleted             .d(A).pnext<=.d(B).pnext             .MFree B       }       Function PopOne {             Read &A             If .IsNull(A) Then Error "Invalid Pointer"             =.d(A)             B=.d(A).pnext             .MFree A             A=B       } } Return