Παρασκευή, 8 Ιανουαρίου 2021

Νέο κεφαλαιο στο βιβλίο Μαθαίνω Προγραμματισμό

Το βιβλίο Μαθαίνω Προγραμματισμό συμπλήρωσε 9 κεφάλαια. 

Τα κεφάλαια που έχουν γραφτεί:

1. Το πρώτο μου πρόγραμμα!

2. Παιχνίδι: Μάντεψε έναν Αριθμό

3. Το κόσκινο του Ερατοσθένη

4. Το δένδρο του Πυθαγόρα

5. Παραγγελία Αυγών

6. Απόσταση Λιβενστέϊν

7. Ακολουθία Φιμπονάτσι με πολύ μεγάλους αριθμούς

8. Δυαδικός Αθροιστής

9. Ψηφιοποίηση Δεδομένων (Ελεύθερο Σχέδιο)

Το 9ο κεφάλαιο έχει 19 σελίδες (είναι το μεγαλύτερο). Έχει ένα πρόγραμμα με τρεις παραλλαγές (βάσει ολοκλήρωσης). Φτιάχνουμε με Ελεύθερο Σχέδιο σχήματα και τα καταχωρούμε και την αναπαράγουμε. Φτιάχνουμε τα εργαλεία για το Ελεύθερο Σχέδιο.

Κατεβάστε το βιβλίο εδώ!


Πέμπτη, 7 Ιανουαρίου 2021

Αναθεώρηση 19, Έκδοση 10

 Βρήκα ένα Bug, μυστήριο! Ξεκίνησα σήμερα τον υπολογιστή και δουλεύοντας με τη Μ2000 βγήκε ένα πρόβλημα που κανονικά δεν έπρεπε να υπήρχε! Δηλαδή ο Timecount έδειχνε ότι είχαν περάσει 26 μέρες από την εκκίνηση. Λογικά όταν ξεκινάει ο υπολογιστής ο βασικός μετρητής του ξεκινάει από το μηδέν. Έτσι θα έπρεπε να είχε δείξει το πολύ μισή ώρα (σε χιλιοστά του δευτερολέπτου). Με την υψηλή τιμή η τιμή πήγαινε για καταχώρηση σε έναν Long (32bit) πράγμα που έβγαζε Overflow. To περιβάλλον της Μ2000 το έλεγε αλλά ήταν αδύνατο να συμβαίνει. Οπότε το εκτέλεσα μέσα από το περιβάλλον της VB6 και το διόρθωσα βάζοντας αντί για long τύπο Currency που δεν έχει ανάγκη (έχει μεγάλη ακρίβεια 21 ψηφίων όπου τα τέσσερα είναι δεκαδικά).

Πιθανόν το πρόβλημα να προέρχεται από αναβάθμιση των Windows. Αν και η τελευταία αναβάθμιση όπως θυμάμαι ήταν του Defender την προηγούμενη μέρα. Οπότε λογικά υπήρχε εδώ και 26 μέρες. Πιθανόν τότε έγινε και η αναβάθμιση των Windows 10. Έκανα κράτηση του υπολογιστή και επανεκκίνηση και διορθώθηκε και ο μετρητής Timecount. (αν δεν χρησιμοποιήσουμε το Profiler δεν μηδενίζει και δείχνει την διάρκεια που εκτελείται το λειτουργικό, δηλαδή από την εκκίνηση).




Πέμπτη, 24 Δεκεμβρίου 2020

Ανανέωση του βιβλίου Μαθαίνω Προγραμματισμό

Το βιβλίο Μαθαίνω Προγραμματισμό συμπλήρωσε 8 κεφάλαια. Θα συνεχιστεί η προσθήκη. Απαιτείται η τελευταία έκδοση 10 με την τελευταία αναθεώρηση 18 (ειδικά για το 8ο κεφάλαιο, γιατί βρήκα bug και το διόρθωσα).

Τα κεφάλαια που έχουν γραφτεί:

1. Το πρώτο μου πρόγραμμα!

2. Παιχνίδι: Μάντεψε έναν Αριθμό

3. Το κόσκινο του Ερατοσθένη

4. Το δένδρο του Πυθαγόρα

5. Παραγγελία Αυγών

6. Απόσταση Λιβενστέϊν

7. Ακολουθία Φιμπονάτσι με πολύ μεγάλους αριθμούς

8. Δυαδικός Αθροιστής

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

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

Υπάρχουν πολλά ιστορικά θέματα που σχετίζονται με τα κεφάλαια. Ειδικά στο κεφάλαιο 8 υπάρχουν τέσσερις σελίδες με την Ιστορία των Υπολογιστικών Μηχανών.

Ακόμα δεν έχω ιδέα για τα επόμενα κεφάλαια. Αν υπάρχει κάποια σκέψη ας γραφτεί εδώ!

Κατεβάστε το βιβλίο εδώ!


Τετάρτη, 23 Δεκεμβρίου 2020

Revision 18, Version 10

 A Bug removed so a Boolean type now written as boolean and not as string when push to stack of values, like in a multi assignment as in this example:

def k(a, b)=(a<10, b<5)
(a, b)=k(1, 20)
Print a, b

Σάββατο, 19 Δεκεμβρίου 2020

Μαθαίνω Προγραμματισμό - Τα πρώτα κεφάλαια!

Ξεκίνησα ένα βιβλίο εκπαιδευτικό για τον προγραμματισμό. Προς το παρόν έχει 7 κεφάλαια. Κάθε κεφάλαιο είναι ένα πρόγραμμα λίγων γραμμών. (22-12-20 μπήκαν τα κεφάλαια 6ο και 7ο, και αφιέρωση στην εγγονή μου Κατερίνα που έχει σήμερα γενέθλια)

Χρειάζεται να χρησιμοποιήσει ο εκπαιδευόμενος το περιβάλλον της Μ2000 και να ακολουθήσει τις οδηγίες. Υπάρχουν και ασκήσεις.

Όποιος τελειώσει το τέταρτο κεφάλαιο θα μπορεί με λίγη τύχη να φτιάξει αυτό εδώ:



Κατεβάστε το βιβλίο εδώ!

Τρίτη, 8 Δεκεμβρίου 2020

Καμπύλη Πεάνο

 

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

Το πρόγραμμα που δημιούργησε τηνjpg εικόνα αυτή είναι αυτό:

move 0,0
image "g1.emf"
a$=""
copy scale.x, scale.y to a$
image a$ export "g1.jpg", 80

Πήρε την εξαγωγή του επόμενου προγράμματος, που ήταν σε emf (σχέδιο, είναι μέγεθος 32MB, γιατί έχει όλες τις εντολές γραμμών, και εδώ το σχέδιο γίνεται με πάρα πολλές γραμμές), το εμφάνισε στην κονσόλα της Μ2000, το αντέγραψε ως Bitmap, και το έσωσε ως jpg.


Και αυτό εδώ είναι ο Εξερευνητής Πεάνο. Λογικά όλοι δημιουργούν μια καμπύλη Πεάνο με χρήση ακεραίων. Εδώ γίνεται χρήση διπλής ακρίβειας πραγματικών. Το σχέδιο γίνεται μέσα σε ένα μπλοκ Drawing (Σχέδιο) το οποίο παραδίδει μια διάρθρωση μνήμης, εδώ το B. Με μια Len(B) η Μήκος() μπορούμε να δούμε το μήκος σε Bytes. Εδώ έχει 32 εκατομμύρια Bytes. Στο τέλος την γράφουμε ως δυαδικό αρχείο, με τρεις εντολές Open, Put, Close. Η διάρθρωση B έχει φτιαχτεί ως emf, από την εντολή Drawing, δηλαδή σαν ένα αρχείο στη μνήμη. Έτσι η  εντολή εικόνα ή Image μπορεί να την διαχειριστεί.

Όπως βλέπουμε η καμπύλη δημιουργείται από μια ρουτίνα (υποπρόγραμμα), που καλεί τον εαυτό του (recursion, αναδρομή).

Την ρουτίνα την πήρα από εδώ: http://rosettacode.org/wiki/Peano_curve στην οποία έχω αναρτήσει και ένα πιο απλό πρόγραμμα!


Module Peano_Explorer {
      cls 1, 0
      once=true
      Flush
      data 3.9, 3
      for i= 3.4 to 4 step 0.018
            data i
      next i
      Drawing scale.x, scale.y {
            while not empty
                  read k
                  wi=k^4
                  n=twipsX*int(K/3*12)
                  wi2=wi*(3/k)^1.5
                  (dx, dy)=((scale.x-wi2*n)/2, (scale.y-wi2*n)/2)
                  move dx , dy
                  if once then fill wi2*n, wi2*n, 0
                  dx+=k*1.2*twipsX
                  dy+=k*1.2*twipsY
                  move dx, dy
                  if not once then Pen 15-int((k mod 0.7)*8+2) {Peano(0,0,wi,0, 0)}
                  once=false
                  next
            end while
      } as B
      Move 0,0
      image B
      open "g1.emf" for output as #f
      put #f, B
      close #f

      Sub Peano(x, y, lg, i1, i2)
            If lg ==1 Then
                  Draw to x*n+dx , y*n+dy
                  Exit sub
            end if
            lg/=k
            Peano(x+2*i1*lg, y+2*i1*lg, lg, i1, i2)
            Peano(x+(i1-i2+1)*lg, y+(i1+i2)*lg, lg, i1, 1-i2)
            Peano(x+lg, y+lg, lg, i1, 1-i2)
            Peano(x+(i1+i2)*lg, y+(i1-i2+1)*lg, lg, 1-i1, 1-i2)
            Peano(x+2*i2*lg, y+2*(1-i2)*lg, lg, i1, i2)
            Peano(x+(1+i2-i1)*lg, y+(2-i1-i2)*lg, lg, i1, i2)
            Peano(x+2*(1-i1)*lg, y+2*(1-i1)*lg, lg, i1, i2)
            Peano(x+(2-i1-i2)*lg, y+(1+i2-i1)*lg, lg, 1-i1, i2)
            Peano(x+2*(1-i2)*lg, y+2*i2*lg, lg, 1-i1, i2)
      End Sub
}

Παρασκευή, 4 Δεκεμβρίου 2020

Αναθεώρηση 12, Έκδοση 10 και ένα πρόγραμμα!

Μια μικρή διόρθωση στην αναθεώρηση 12. Όταν έβαλα το xmldata αντικείμενο, άλλαξα ένα εσωτερικό αντικείμενο mHandler, να μπορεί να βγει στη Μ2000, όχι όμως όλη η λειτουργικότητά του. Αυτό το τελευταίο επιτυγχάνεται με δηλώσεις τύπου Friend στις μεθόδους-ιδιότητες. Όταν λοιπόν εσωτερικά πρέπει να χρησιμοποιήσω αυτές τις ιδιότητες, τότε το αντικείμενο πρέπει να είναι mHandler, και όχι ένα Object που να είναι mHandler (επειδή το object έχει το dispatch interface, και αυτό εμφανίζει μόνο τις Public ιδιότητες). To mHandler είναι φορέας άλλων αντικειμένων, σε μια ιδιότητα objref, και αυτή είναι friend. Έτσι όπου χρησιμοποιούσα το mHandler ως οbject (το γενικό αντικείμενο που παίρνει τα πάντα), απλά το έγραψα σε μια μεταβλητή τύπου mHandler, οπότε αυτή χρησιμοποιεί το mHandler interface, άρα βλέπει τις Friend). Τα enumarators, λειτουργούν με φορέα το mHander, οπότε κάπου ξέχασα στη μετατροπή και άφησα ένα  objref το οποίο δεν έπρεπε να ήταν εκεί. Το module που χτύπησε στο info, ήταν το H1 το οποίο τώρα τρέχει!  Το ίδιο module (τμήμα) υπάρχει εδώ: https://georgekarras.blogspot.com/2018/11/oop-group-as-enumaration-variable-with.html


Χθες έγραψα σε ένα task στο rosetta.org, στο http://rosettacode.org/wiki/Amb

Δεν το έχω βρει το θέμα του Amb, που βγαίνει από τη λέξη ambiguous, αμφίσημος. Στο task (έργο) καλούμαστε να φτιάξουμε το χειριστή Amb ή κάποια συνάρτηση που να κάνει την ίδια δουλειά. Πραγματικά μου είναι δύσκολο να το περιγράψω στα ελληνικά!

Ένας ψευδοκώδικας δείχνει την λειτουργία του:

let x = Amb(1, 2, 3)
let y = Amb(7, 6, 4, 5)
Amb(x * y = 8)
print x, y

Το Amb πρέπει να βρει τα x και y που ικανοποιούν την συνθήκη x*y=8.

Το συγκεκριμένο έργο ζητάει από τα τέσσερα παρακάτω (δηλαδή σαν να έχουμε τέσσερις λίστες, ή πίνακες), να βρεθούν οι τέσσερις λέξεις που δίνουν "that thing grows slowly" και η συνθήκη λέει ότι κάθε λέξη με τις διπλανές τις θα έχει το ίδιο γράμμα, έτσι το thing ξεκινάει με t όπως τελειώνει το that, και τελειώνει σε g όπως ξεκινάει το grows.

  1. "the" "that" "a"
  2. "frog" "elephant" "thing"
  3. "walked" "treaded" "grows"
  4. "slowly" "quickly"

Το ενδιαφέρον εδώ δεν είναι να δώσουμε ένα πρόγραμμα με προκαθορισμένες επαναλήψεις. Για το λόγο αυτό λέγεται και ανήκει στο "non-deterministic finite automaton". Εδώ τα δεδομένα πρέπει να καθορίσουν τον αριθμό ελέγχων, ή ακόμα και επαναλήψεων.

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

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

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

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

Στο πρώτο πρόγραμμα ο έλεγχος γίνεται σε μια λάμδα που ζητάει πίνακες (ενός στοιχείου) οπότε μέσα στη λάμδα αυτή έχουμε δώσει το τρόπο που θα διαβαστούν οι τιμές με τις συνοδευτικές συναρτήσεις #Val() ή #Val$() αντίστοιχα. Το # στο όνομα δηλώνει ότι αυτές εφαρμόζονται στον αυτόματο πίνακα, ή tuple, (είναι ένα είδος linq στην Μ2000, δηλαδή σαν μια ενσωματωμένη γλώσσα ερωτημάτων, και υπάρχουν αρκετές, μερικές από τις οποίες δίνουν νέα tuple, και έτσι μπορούμε να έχουμε μια σειρά από αυτές τις "συνοδευτικές" συναρτήσεις, και να εξάγουμε στοιχεία υπό κάποια διαμόρφωση).

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

Module AmbFunction {
      Function Amb (failure) {
            // get an array of s items, return an array of 1 item
            // we do this so we forget the type of element
            getitem=lambda (n, c) -> {
                  dim z(1) :link c to c()
                  stock c(n) keep 1, z(0) // copy from c(n) to z(0) one item
                  =z()
            }
            read a
            c1=lambda a, getitem (i, &any, &ret) ->{
                  any=getitem(i, a)
                  ret=any
                  =true
            }
            m=stack.size
            if m=0 then Error "At least two arrays needed"
            c=c1
            while m>1 {
                  read b
                  c1=lambda c2=c, j=0, m=len(a), b, failure, getitem (i, &any, &ret) ->{
                        any=getitem(i, b)
                        ret=(,) : ok=false : anyother=(,)
                        do
                              if c2(j, &anyother, &ret) then
                                    if not failure(any, anyother) then
                                          ok=true
                                          ret=cons(ret, any)
                                    end if
                              end if
                              j++
                        until ok or j=m
                        if j=m then j=0
                        =ok
                  }
                  c=c1 : a=b : m--
            }
            read b
            amb1=lambda c2=c, j=0, m=len(a), b, failure, getitem (&ret) ->{
                  ret=(,) : ok=false: anyother=(,)
                  k=each(b)
                  while k
                        any=getitem(k^, b)
                        do
                              if c2(j, &anyother, &ret) then
                                    if not failure(any, anyother) then
                                          ok=true
                                          ret=cons(ret, any)
                                    end if
                              end if
                              j++
                        until ok or j=m
                        if j=m then j=0
                        if ok then exit
                  end while
                  =ok
            }
            ret=(,)
            if amb1(&ret) then =ret
      }

      a=(1, 2, 3)
      b=(7, 6, 4, 5)
      failure=lambda (a,b)->{
            =a#val(0)*b#val(0)<>8
      }
      Print amb(failure, a, b)#str$()
      a=("the", "that", "a")
      b=("frog", "elephant", "thing")
      c=("walked", "treaded", "grows")
      d=("slowly", "quickly")
      failure=lambda (a,b)->{
            =left$(a#val$(0),1)<>right$(b#val$(0),1)
      }
      Print amb(failure, a, b, c, d)#str$()
}
AmbFunction


2 4
that thing grows slowly


Η δεύτερη λύση

Module AmbFunction {
      Enum Solution {First, Any=-1}
      Function Amb(way as Solution, failure) {
            // get an array of s items, return an array of 1 item
            // we do this so we forget the type of element
            getitem=lambda (n, c) -> {
                  dim z(1) :link c to c()
                  stock c(n) keep 1, z(0) // copy from c(n) to z(0) one item
                  =z()
            }
            read a
            c1=lambda i=0, a, getitem (&any, &ret) ->{
                  any=getitem(i, a)
                  ret=any
                  i++
                  ok=i=len(a)
                  if ok then i=0
                  =ok
            }
            m=stack.size
            if m=0 then Error "At least two arrays needed"
            c=c1
            while m>0 {
                  read a
                  c1=lambda c2=c, i=0, a, getitem (&any, &ret) ->{
                        any=getitem(i, a)
                        ret=(,) : ok=false : anyother=(,)
                        ok=c2(&anyother, &ret)
                        ret=cons(ret, any)
                        if ok then i++
                        ok=i=len(a)
                        if ok then i=0
                        =ok
                  }
                  c=c1 : m--
            }
            ok=false
            any=(,)
            flush
            while not ok
                  ret=(,)
                  ok=c(&any, &ret)
                  s=stack(ret)
                  if not failure(! s) then data ret : if way>0 then ok=true
            End While
            if empty then
                   ret=(("",),)
             else
                    ret=array([])
             end if
             =ret
      }

      a=(1, 2, 3)
      b=(7, 6, 4, 5)
      failure=lambda (a,b)->{
            =a*b<>8
      }
      Print Amb(First, failure, a, b)#val(0)#str$()
      a=("the", "that", "a")
      b=("frog", "elephant", "thing")
      c=("walked", "treaded", "grows")
      d=("slowly", "quickly")
      failure=lambda (a$, b$, c$, d$)->{
            def amb(x$, y$)=right$(x$,1)<>left$(y$,1)
            =amb(a$,b$) or amb(b$,c$) or amb(c$, d$)
      }
      Print amb(First, failure, a, b, c, d)#Val(0)#str$()
      Range=lambda (a, f) ->{
            for i=a to f-1: data i: next
            =array([])
      }
      Print "Small Pythagorean triples problem:"
      a=range(1,11)
      b=a
      z=a
      failure=lambda (a, b, z)->{
            =not (a^2+b^2=z^2)
      }
      all=amb(Any,failure, a, b, z)
      k=each(all)
      while k
            z=array(k)
            Print z#str$()
      end while

}
AmbFunction

2 4
that thing grows slowly
Small Pythagorean triples problem:
4 3 5
3 4 5
8 6 10
6 8 10