Τετάρτη 23 Σεπτεμβρίου 2020

Τομή δύο γραμμών (χρήση OOP)

Το πρόγραμμα περιέχει ένα τμήμα το οποίο δέχεται δυο αυτόματους πίνακες (tuple) που ο καθένας περιέχει τέσσερις αριθμούς, ή δυο σημεία στο επίπεδο όπου ορίζουν μια γραμμή. Δηλαδή δίνουμε στο τμήμα Τομή2Γραμμών δυο γραμμές με δυο σημεία για κάθε γραμμή (τα σημεία πρέπει να είναι διαφορετικά σε μια γραμμή, δηλαδή δεν ορίζουμε γραμμή με ένα σημείο που το δίνουμε δυο φορές, ή δυο σημεία όπου έχουν το ίδιο Χ αλλά διαφορετικό Ψ).

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

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

Η μέθοδος Φ(χ) δίνει το ψ για κάθε χ. Η μέθοδος Τομή(άλλη_γραμμή) δέχεται ένα αντικείμενο γραμμή, και δίνει έναν αυτόματο πίνακα. Αυτός ο πίνακας μπορεί να μην έχει στοιχεία, και αυτό σημαίνει "απροσδιόριστο" ή να έχει δυο στοιχεία, το σημείο τομής.

Απροσδιόριστο έχουμε αν οι γραμμές είναι παράλληλες, είτε συμπίπτουν είτε όχι. Αν συμπίπτουν τότε δεν υπάρχει ένα σημείο τομής, όλα τα σημεία της μιας γραμμής είναι σημεία και της άλλης. Αν δεν συμπίπτουν τότε δεν υπάρχει σημείο τομής (στα μαθηματικά δυο παράλληλες πιθανόν να τέμνονται σε άπειρη απόσταση). Ουσιαστικά ο έλεγχος γίνεται στις κλίσεις των γραμμών με τελεστή == ο οποίος ελέγχει την ισότητα με περιορισμένη ακρίβεια (13 δεκαδικών) και όχι 15-16 που έχουν οι διπλής ακριβείας. Αυτό σημαίνει ότι ενδέχεται να έχουμε διαφορετικές κλήσεις αλλά να είναι πολύ κοντά, σχεδόν παράλληλες οι γραμμές, οπότε τις δεχόμαστε ως παράλληλες. Τυπικά το σημείο τομής σε αυτή τη περίπτωση "σχεδόν παράλληλες γραμμές" είναι κοντά στο άπειρο, δεν έχει δηλαδή νόημα να γνωρίζουμε τον αριθμό! Έτσι τιμή απροσδιόριστο επιστρέφει όταν έχουμε παράλληλες ή σχεδόν παράλληλες γραμμές.

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

Το Πρόγραμμα

Τμήμα Τομή2Γραμμών (Α_2Σημεία, Β_2Σημεία) {

      Κλάση Γραμμή {
      ιδιωτικό:
            κλίση, σταθερά
      δημόσιο:
            Συνάρτηση Φ(χ) {
                  =χ*.κλίση-.σταθερά
            }
            Συνάρτηση Τομή(άλλη_γραμμή ως Γραμμή) {
                  αν άλλη_γραμμή.κλίση==.κλίση τότε
                        =(,)
                  αλλιώς
                        χ1=(.σταθερά-άλλη_γραμμή.σταθερά)/(.κλίση-άλλη_γραμμή.κλίση)
                        =(χ1, .Φ(χ1))
                  τέλος αν
            }
      Κλάση:
            Τμήμα Γραμμή (χ1, ψ1, χ2, ψ2) {
                  αν χ1==χ2 τότε λάθος "λάθος στην εισαγωγή"
                  αν χ1>χ2 τότε άλλαξε χ1,χ2 : άλλαξε ψ1, ψ2
                  .κλίση<=(ψ1-ψ2)/(χ1-χ2)
                  .σταθερά<=χ1*.κλίση-ψ1
            }
      }
      ΓραμμήΑ=Γραμμή(!Α_2Σημεία)
      ΓραμμήΒ=Γραμμή(!Β_2Σημεία)
      Αποτέλεσμα=ΓραμμήΑ.Τομή(ΓραμμήΒ)
      τύπωσε "Τομή:",
      αν μήκος(Αποτέλεσμα)=0 τότε
            τύπωσε "Απροσδιόριστο"
      αλλιώς
            τύπωσε Αποτέλεσμα
      τέλος αν
}
Τομή2Γραμμών (4,0,6,10), (0,3,10,7) '    5  5
Τομή2Γραμμών (0,0,1,1), (1,4,2,5) ' Απροσδιόριστο
Τομή2Γραμμών (0,0,1,1), (0,0,1,1) ' Απροσδιόριστο


Περί του Διερμηνευτή:

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

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

Ονομάζουμε τα tuple ως αυτόματους πίνακες γιατί είναι πίνακες που ορίζονται άμεσα σε έκφραση, και όχι με ειδική εντολή όπως Πίνακας, Τοπικό, Τοπική, Τοπικές ή Γενικό, Γενική, Γενικές.

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

Οι αυτόματοι πίνακες στη Μ2000 μπορούν να αλλάξουν τιμή. Πχ το Αποτέλεσμα+=100 αυξάνει κατά 100 όλα τα αριθμητικά μέλη του πίνακα. Το Αποτέλεσμα#Τιμή(0) δίνει την αριθμητική τιμή του πρώτου στοιχείου (όλοι οι αυτόματοι πίνακες ξεκινούν από το 0). Υπάρχουν αρκετές συναρτήσεις με # στην αρχή του ονόματος οι οποίες είτε δίνουν τελικό αποτέλεσμα είτε δίνουν αποτέλεσμα πίνακα και πάνω σε αυτό δίνουμε άλλη συνάρτηση, πχ στο παρακάτω από τα 1,2,3,4 βγάζουμε ένα πίνακα με στοιχεία από το 3ο (δείκτης 2) έως το 4ο (δείκτης 3), και από αυτόν τον πίνακα υπολογίζουμε το άθροισμα. Υπάρχουν συναρτήσεις με το # στην αρχή του ονόματος οι οποίες δέχονται λάμδα συναρτήσεις για να παράγουν έναν πίνακα ή ένα τελικό αποτέλεσμα.

Τύπωσε (1,2,3,4)#Μέρος(2,3)#Αθρ()=7

Δείτε παρακάτω ότι έχουμε φτιάξει εκφράσεις που φτιάχνουν πίνακες οι οποίοι εξαφανίζονται μετά (δεν κρατάμε κάποιον δείκτη). 

Τύπωσε "(3:4)"="("+(1,2,3,4)#Μέρος(2,3)#Γραφή$(":")+")"

Τύπωσε "("+(1,2,3,4)#Μέρος(2,3)#Γραφή$(":")+")"="(3:4)"

Επίσης δείτε ότι ο διερμηνευτής βρίσκει ότι το (1,2,3,4)#Μέρος(2,3)#Γραφή$(":") είναι αλφαριθμητικό χωρίς να το εκτελέσει λόγω του $ (και των κανόνων που ακολουθεί), οπότε το "("+...+")" το βλέπει σαν αλφαριθμητική έκφραση. Η Τύπωσε βρίσκει ότι έχει λογική έκφραση για δυο εκφράσεις αλφαριθμητικές. Ο διερμηνευτής πριν εκτελέσει την λογική έκφραση έχει "δει" μπροστά ότι έχει λογική έκφραση, χωρίς να ενδιαφέρεται για το τι υπάρχει μέσα σε παρενθέσεις, αλλά τι τελεστές υπάρχουν εκτός και αν υπάρχουν σε ονόματα (εκτός παρενθέσεων) το $. Επίσης από τα "" ή {} βλέπει επίσης αν έχει αλφαριθμητικά. Οι αγκύλες χρησιμοποιούνται για αλφαριθμητικά πολλαπλών γραμμών και για μπλοκ κώδικα. Ο χρωματιστής εντολών βρίσκει τι περίπτωση έχουμε γιατί ξέρει ποιες εντολές δέχονται μπλοκ κώδικα. Επίσης ένα γυμνό μπλοκ (χωρίς εντολή στην αρχή), το θέτει ο χρωματιστής άμεσα ως μπλοκ κώδικα επειδή δεν ξεκινάνε ποτέ σταθερές αλφαριθμητικών σε γραμμές εντολών (πάντα βρίσκονται σε εκφράσεις).

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

Κάποια στιγμή ο Διερμηνευτής θα αποκτήσει AST και τότε δεν θα έχει σημασία πως εμφανίζεται στο κείμενο η κάθε δομή (όπως αυτή της επανάληψης με την Ενώ), ως προς το χρόνο εκτέλεσης. Προς το παρόν ο διερμηνευτής εκτελεί πηγαίο κώδικα, που τεμαχίζει, κάθε φορά, και σχηματίζει δομές δεδομένων και τις καταργεί όποτε χρειάζεται. Ο διερμηνευτής είναι "καταναλωτής" κώδικα. Όταν καταναλωθεί ο κώδικας τότε τερματίζει. Μια εντολή Έξοδος διαγράφει τον υπόλοιπο κώδικα. Ένα μπλοκ κώδικα "τεμαχίζει" τον κώδικα, και εκτελείται σαν να είναι μόνος του.



Δευτέρα 21 Σεπτεμβρίου 2020

Αναθεώρηση 55 'Εκδοση 9.9

 Έγιναν μικρές διορθώσεις, και προστέθηκαν:

3 συναρτήσεις στους αυτόματους πίνακες (tuple)

2 αντικείμενα: ShellPipe και SerialPort.

Το ShellPipe χρησιμεύει για να εκκινήσουμε μια εφαρμογή των windows σε κονσόλα, χωρίς να φαίνεται η κονσόλα και με την εξαγωγή να την παίρνουμε στο πρόγραμμά μας (Μ2000) και από αυτό να μπορούμε να στέλνουμε επίσης οδηγίες. Υπάρχουν στο info τρία παραδείγματα:

Handler - χωρίς τη χρήση του ShellPipe αλλά με τη χρήση του WScript.Shell αντικειμένου και την μέθοδο Exec, ανοίγουμε το cmd.exe και επειδή με αυτό το τρόπο εμφανίζεται η κονσόλα με το cmd.exe (χωρίς όμως να δείχνει κάτι, επειδή ό,τι έχει μας το δίνει στο πρόγραμμα), χρησιμοποιούμε δυο συναρτήσεις του API Win32 για να πιάσουμε το παράθυρο και να το ελαχιστοποιήσουμε στην γραμμή εργασιών. (αυτό τρέχει και σε παλαιότερες εκδόσεις).

Handler2 -  με χρήση του ShellPipe. Τώρα η εφαρμογή cmd.exe δεν φαίνεται στη γραμμή εργασιών, δεν ανοίγει καν το παράθυρο. Όλος ο χειρισμός γίνεται εσωτερικά. Χρησιμοποιούμε και γεγονός για να τραβάμε τα στοιχεία "εξόδου".

Chessgame - με χρήση του ShellPipe (το είχα φτιάξει και με το πρώτο τρόπο αλλά δεν μου άρεσε που φαίνονταν για μερικά χιλιοστά του δευτερολέπτου η κονσόλα της καλούμενης εφαρμογής). Εδώ καλούμε μια μηχανή σκακιού. Η μηχανή μπορεί να κατέβει από εδώ https://stockfishchess.org/

Χρησιμοποίηση τη stockfish 12, για λιγότερο από δευτερόλεπτο, και παίζει πάρα πολύ καλά (παίζει με τα μαύρα). Το ωραίο με το chessgame είναι ότι μπορούμε να πάμε όσες κινήσεις θέλουμε πίσω. Επειδή λειτουργεί εσωτερικά με σειρές FEN,ήταν πολύ εύκολο να συνδέσω την μηχανή για σκάκι. Τις κινήσεις στη σκακιέρα τις ελέγχει το πρόγραμμα σε Μ2000. Όταν είναι να παίξει ο μαύρος, τότε στέλνει την σειρά FEN στη μηχανή και με αίτημα για μερικές εκατοντάδες χιλιοστά του δευτερολέπτου, γυρνάει την απάντηση. Επειδή τις κινήσεις τις γράφει στη φόρμα, αν δεν δούμε τη κίνηση του μαύρου, τη διαβάζουμε από κάτω. Όταν πηγαίνουμε κινήσεις πίσω τότε όταν θα έρθει η σειρά του μαύρου θα παίξει η μηχανή.
Δεν έχω καταφέρει να νικήσω την μηχανή...(είναι πολύ καλή). Αν ο υπολογιστής είναι αργός ενδέχεται να χάσει η μηχανή, γιατί για ίσο χρόνο ο αργός θα δώσει χειρότερη απάντηση.


Το SerialPort, είναι αντικείμενο για να χειριζόμαστε με απλό τρόπο σειριακές πόρτες. Σκοπός του αντικειμένου είναι να συνδέουμε μια πλακέτα με Arduino. Το έχω δοκιμάσει και δουλεύει τόσο για να διαβάζει όσο και για να γράφει (τα κάνει και τα δύο μαζί). Το Arduino στέλνει δεδομένα σε UTF8, οπότε στο πρόγραμμα Arduino δείχνω πως διαβάζω από τη σειριακή, πως βρίσκω την αλλαγή γραμμής, πως χωρίζω το 'πακέτο" που διάβασα βάσει της αλλαγής γραμμής. Εδώ επειδή έχουμε UTF8, οι χαρακτήρες κάτω από 128 είναι του ενός byte.

Χρησιμοποιήθηκε το παρακάτω σε ένα Arduino NodeMCU 0.9 (ESP-12 Module).

int incomingByte = 0; // for incoming serial data

void setup() {
  Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}

void loop() {
  // send data only when you receive data:
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();

    // say what you got:
    Serial.print("I received: ");
    Serial.println(incomingByte, DEC);
  }
  // δοκιμή και χωρίς τη γραμμή παρακάτω
  Serial.println("Wait Γιώργο....");
}




Δευτέρα 14 Σεπτεμβρίου 2020

A LED CLOCK

This is a LED clock. Using Desktop statement we can make one of standard colors (0 to 15) as transparent. Here we make Black as transparent.






Module Led_Clock{
      Escape Off
      Smooth off
      Dim D(-1 to 9)
      D(-1)=(0,0,0,0,0,0,0)
      D(0)=(1,1,1,0,1,1,1)
      D(1)=(0,0,1,0,0,1,0)
      D(2)=(1,0,1,1,1,0,1)
      D(3)=(1,0,1,1,0,1,1)
      D(4)=(0,1,1,1,0,1,0)
      D(5)=(1,1,0,1,0,1,1)
      D(6)=(1,1,0,1,1,1,1)
      D(7)=(1,0,1,0,0,1,0)
      D(8)=(1,1,1,1,1,1,1)
      D(9)=(1,1,1,1,0,1,1)
      N=180
      XX=(scale.x-N*75) div 2
      YY=scale.y-N*22
      NN=N
      BackColor=0
      CLS BackColor,0
      desktop 255, BackColor
      Forecolor=12
      C=BackColor-Forecolor
      pen BackColor
      for i=0 to 9: cc=d(i): cc*=c:next
      m=1
      move XX+N*23.2, YY+N*5.2
      polygon BackColor-C, N,-N, N,N, -N, N, -N, -N
      move XX+N*23.2,YY+N*13.2
      polygon BackColor-C, N,-N, N,N, -N, N, -N, -N
      move XX+N*49.2,YY+N*5.2
      polygon BackColor-C, N,-N, N,N, -N, N, -N, -N
      move XX+N*49.2,YY+N*13.2
      polygon BackColor-C, N,-N, N,N, -N, N, -N, -N
      dsk=True
      every 1000/2 {
            k=now
            k1=val(str$(k, "hh"))
            k2=val(str$(k, "nn"))
            k3=val(str$(k, "ss"))
            LED(XX, D(k1 div 10))
            LED(XX+N*12, D(k1 mod 10))
            LED(XX+N*26, D(k2 div 10))
            LED(XX+N*38, D(k2 mod 10))
            LED(XX+N*52, D(k3 div 10))
            LED(XX+N*64, D(k3 mod 10))
            refresh 1000
            if keypress(32) then
                  dsk~
                  if dsk then       desktop 255 else desktop 255, BackColor
            end if
            if keypress(27) or mouse=2 then exit
      }
      desktop 255
      pen 14
      refresh 50
      wait 1000
      Escape On
      sub LED(XX, S())
            move XX+N*1.2, YY+NN
            \\ LED  - UPPER
            polygon BackColor-S(0), N,-N,N*6,0, N,N, -N, N,-N*6,0, -N, -N
            \\ LED | LEFT UPPER
            move XX+N*1.2-N*1.2, YY+N*1.2+NN
            polygon BackColor-S(1), N,-N,N,N,0,N*6,-N, N, -N, -N, 0, -N*6
            move XX+N*1.2+N*7.2, YY+N*1.2+NN
            \\ LED | RIGHT UPPER
            polygon BackColor-S(2), N,-N,N,N,0,N*6,-N, N, -N, -N, 0, -N*6
            move XX+N*1.2, YY+N*8.4+NN
            \\ LED - MIDDLE
            polygon BackColor-S(3), N,-N,N*6,0, N,N, -N, N,-N*6,0, -N, -N
            \\ LED | LEFT BOTTOM
            move XX+N*1.2-N*1.2, YY+N*9.6+NN
            polygon BackColor-S(4), N,-N,N,N,0,N*6,-N, N, -N, -N, 0, -N*6
            \\ LED | RIGHT BOTTOM
            move XX+N*1.2+N*7.2, YY+N*9.6+NN
            polygon BackColor-S(5), N,-N,N,N,0,N*6,-N, N, -N, -N, 0, -N*6
            \\ LED - BOTTOM
            move XX+N*1.2, YY+N*16.8+NN
            polygon BackColor-S(6), N,-N,N*6,0, N,N, -N, N,-N*6,0, -N, -N
      end sub
}
Led_Clock