Σάββατο 13 Απριλίου 2019

Binaries Revision 17b Version 9.8

M2000.dll is the same as Revision 17. We have new M2000language.exe, the installation program, which have a newer version of help2000.mdb, the help file.

In help2000.mdb we have a better program for using the Structure statement for reading database structure. We can read all tables and one index with any number of keys (each key may use ascending or descending order).
We can create databases only by use SQL, but the old statements can be used for making simple databases.

Πέμπτη 11 Απριλίου 2019

Revision 17 Version 9.8

Restore a change in version 9.7 rev 21+, which broke calling "move" method for user forms, by code. Because alternative statements do the right job, this fault stayed there for some revisions.


Code which move and resize window without use the "move" method (but internal use direct call to the proper move method). This method also center the window (the semi colon specify this)

declare form1 form
Layer Form1 {
 window 16, 15000,12000;
 cls #333333
}
method form1, "show",1
declare form1 nothing


And this is the broken code which now works. This code include the change of form's caption (is optional for this example, may excluded).


declare form1 form
with form1,"title","mytitle"
method form1, "move", 100,100,10000,8000
method form1, "show", 1
declare form1 nothing

Τετάρτη 10 Απριλίου 2019

OOP another example in M2000

Here is an example of using OOP in M2000. This code can be copied in a module A (in M2000 console we write Edit A and then editor open, so we just copy this and press Esc to exit editor). We run the code calling A (just write A and press enter).
M2000 Interpreter can be use objects of type Group. This type is the user object. A group is a prototype kind of object. A group is a value type, but we can define pointers to groups also. A pointer to group is one of two kinds, a real pointer which hold alive a group, and a weak reference type. In any of these two kinds we can change the pointer to point to another group, or to an empty group (using A->0&  a long value indicate the empty group to thin arrow operator).
A group may have a name, like Bob or John, and we say we have a named group. This type is like a static object in other languages, and life cycle end when the environment where we create it end or exit from execution. So named groups exist if the creator is in a running state. We can make global groups also, which are exist the same as the local ones but they can used from modules/functions which we can call (we can define modules and functions in modules and functions). Scope in M2000 has two levels and a half. One is the global scope, then another one is the local scope, and there is a half one, the members scope. So in a group's member Function ToString$ the .name$ is This.name$ and This is the object (instance and object is the same in m2000).

This example use a Big module which have a local CheckIt module. We can keep the inner module and we can copy it in a module B, then we can execute it again (use a Flush statement to empty the stack or delete the Push statement which feed the stack).
When Big module called, there is no CheckIt module, until the definition executed, which M2000 do the simple action to copy it in a list of modules/functions, without check anything. Also notice that a call to Big bound the current stack of values, so parent of Big can pass in and out values through stack. The M2000 Interpreter make a process object and just copy the pointer of parent stack to the new one. This happen again when we call the CheckIt module. So if we say we have the A module running, and the two others, we have three process objects at a running state (process objects used for threads also, but here we didn't use threads).
So now we follow the flow of execution, and interpreter execute the Class statement. This is a macro which replace the code to a function (a global one) with same name as the class name. We can see the code using Print &person() or Edit person()  (we can open editor when the program run, the editor is part of language). The difference in this function is the call to constructor. This is a special call to prevent the deletion of local definitions. We need this when we use a statement like This=Person(... ), because we want all new definitions which merged to This to stay there. So as a summary for class functions, we have a Group statement which make group with all the code inside class, and some logic to execute the constructor ( a module of same name as the group name), and the last statement is the return value, the group as is.

When a named group passed to stack or returned from a function, or assigned to an array item, or an inventory item (a type of list), or in tuple (a kind of array), we place a copy of it, as a float group (an unnamed group). So for these types of use, a group has one and only one internal pointer that can't be shared. We can use names to be references to other named groups, but not to unnamed groups. Such a reference can't get a second one, so a workaround is to use a temporary name, so the next time this name not exist and created again. This can be done in a block for temporary definitions, like the For object_name { }. With this manner we have always groups with only one internal not shared pointer. A float group with a not shared pointer always dies when the container (stack, array, inventory) remove it. In these containers when we assign a new float group, the old one deleted. If we want a group to hold other groups, we can use container type members, and we just put them.

The things get harder if we want to use pointers to groups. A named group always dies at the end of the creator, and a pointer to a named group is always a weak reference, so if we pass the pointer outside of creator, and the creator terminate the pointer will be invalid, but not immediate but the time we use it. A pointer to group is a variable which point to group, and can be copied to other variables (a new one or an already pointer), and in containers. The difference from a named group is that in a variable we can access members using a dot but a thik arrow (=>), and the life of object depends from a pointer counter. If the counter is zero then the object deleted (no more pointers point to object). We can use members of a group as group pointers. We can make a group to point to itself. If it is a named group this isn't a real pointer so we don't have to worry for the life of the object. But if we do this to float group, then the object never die, except of the use a dedicated deconstructor  which we have to call it to reset the pointers to the 0& (empty group). So this is the difficult part when we use multiple pointers to groups.

There is another variant of group, the superclass. When a group created has a null superclass pointer (we can't handle it). When a merge happen the merged group get the superclass pointer of group at the right expression. Because a group may have many groups inside (and each group inside may have other groups). inner groups may have a superclass also, but a different one. A superclass is a named group with no visible members, but has a pointer to the superclass and a flag which indicate that this is a superclass. So when we create a group from superclass we get a group with all members that are not unique to superclass (unique: is a label which we can use before some members of superclass). From group's member functions and modules we can open superclass using For SuperClass { } structure and we can call functions/modules and other members but not the private members. These members can only used from superclass functions and modules. The intent to use a superclass is to make groups to have common properties. Another way to have common properties is by using pointers to groups inside groups, and to use containers, because containers like stack, array, inventory and buffers are reference type, so we can construct objects using the same pointer to array (an identifier without parenthesis used as pointer to a container, and we can make container's items to point to other containers too). So superclass isn't useful but exist as an easy way to demonstrate the use of a referenced base object.

Here in this example we make composition of objects, which we get a pure object, so we don't have a base object. Also M2000 has no use of interfaces, because there are no types for groups, so we can add classes one after the other, in the  root group (the group we make from a class) or the child groups (which we can make inside a group).



Module Big {
        \\ classes are functions which return a concrete object (not a pointer to object)
        \\ by default a class defined in module/function is global, but deleted after exit of module/function
        Module CheckIt  {
                Class person {
                Private:
                        aged=1000
                        name$="Not defined yet"
                Public:
                        Function ToString$ {
                                =.name$+" age"+str$(.aged)
                        }
                        Function IsOld$ {
                                =if$(.aged>65->"Yes", "No")
                        }
                        \\ constructor
                        \\ after Class: all members are exist once, here ate the person() function
                Class:
                        Module Person (.name$, .aged) {}
                } 
                Class player {
                        \\ by default members are public
                        \\ A final function can't be changed from this=person()
                        Function Final IsOld$ {
                                =if$(.aged>35->"Yes", "No")     
                        }
                        \\ constructor
                Class:
                        Module player (name$="Not Defined Yet", age=1000) {
                                this=person(name$, age)   \\ apply inheritence, by merging person() to this
                        }
                }
                \\ variables like Bob and John can get an object, and each create a named group
                \\ these groups erased at the end of this module
                Bob=player("Bob Marley", 36)
                John=player("John Harris", 66)
                Print Bob.ToString$()
                Print "is to old:"+Bob.IsOld$()
                Print John.ToString$()
                Print "is to old:"+John.IsOld$()
                CopyOfBob=Bob
                \\ to alter a private member we have to explicit redefined it
                Group CopyOfBob {
                Private:
                        aged=38
                }
                Print CopyOfBob.ToString$()
                Print "is to old:"+CopyOfBob.IsOld$()
                \\ we can merge CopyOfBob to Bob
                Bob=CopyOfBob
                Print Bob.ToString$()
                Print "is to old:"+Bob.IsOld$()
                Dim Players(10)=Player()
                Players(0):=Bob, John
                For i=0 to 1
                        Print Players(i).ToString$()
                Next
                \\ we can export the array, place a pointer to stack
                Push Players()
        }
        \\ when we call a module we pass current stack as module's current stack
        CheckIt
        \\ now everything in CheckIt are deleted, except the array in current stack
        \\ there are no class functions, but array has all it needs to use
        Read P()
        For i=0 to 9
                Print P(i).ToString$()
                Print "is to old:"+P(i).IsOld$()
        Next
        \\ we can use pointers to objects
        \\ when a pointer created from a no named group, we get a real pointer
        A->p(0)
        Print A=>ToString$(), A=>IsOld$()
        \\ a pointer can point to another object
        A->p(1)
        Print A=>ToString$(), A=>IsOld$()
        \\ we can delete all groups in A()
        Dim A()
        \\ a pointer hold the object
        Print A=>ToString$(), A=>IsOld$()
        \\ we can make a named group (bound to this module), as a copy of A (a pointer to a group)
        John=Group(A)
        Print John.ToString$(), John.IsOld$()
        A->0&   ' we delete the group because no other reference exist for it. A point to an empty group
        A->John  ' this is a pointer as a weak reference to john
        Print A=>ToString$(), A=>IsOld$()
        A->0& ' we delete the weak reference and we place a pointer to an empty group
        Print type$(A)="Group"
        Push John
}
Big
\\ now stack has a group (not a pointer to group, but a group)
Dim A(10)
\\ we can put it in A(3)
Read A(3)
Print A(3).IsOld$()
 

Here is a different approach, we make for each player a pointer to a person group. A group named Person inside Player group return a value as a copy of the A group. Because A is a member of the parent we have to explicit link with a local variable, which maybe have the same name.
See also that we include functions for Player to call functions. For functions can pass the stack using ![]  so if ABC() get a, b we don't get anything in the Person member ABC and in the call we use A=>ABC(![]). The ! place a stack to current stack, and [] is a function which return pointer to a stack, which was the previous stack (so immediate after the execution of [] we get an empty current stack). This mechanism make two things copy the pointer of current stack, and then change the current stack with an empty stack. So no copy or move of items in stack happen and we pass the parameters without any check to ABC in A group (which pointed by A)


Module Big {
        Module CheckIt  {
                Class person {
                Private:
                        aged=1000
                        name$="Not defined yet"
                Public:
                        Function ToString$ {
                                =.name$+" age"+str$(.aged)
                        }
                        Function IsOld$ {
                                =if$(.aged>65->"Yes", "No")
                        }
                Class:
                        Module Person (.name$, .aged) {}
                } 
                Class player {
                Private:
                        Group A
                Public:
                        Group Player {
                                Function IsOld$ {
                                        Link parent A to A
                                        =A=>IsOld$()
                                }
                                value {
                                        Link parent A to A
                                        =Group(A)
                                }
                        }
                        Function ToString$ {
                                =.A=>ToString$()
                        }
                        Function IsOld$ {
                                =.A=>IsOld$()
                        }       
                Class:
                        Module player (name$="Not Defined Yet", age=1000) {
                                Alfa=person(name$, age)   \\ apply inheritence, by merging person() to this
                                Group Alfa {
                                        Function Final IsOld$ {
                                                =if$(.aged>35->"Yes", "No")     
                                        }
                                }
                                .A<=Pointer((Alfa))
                        }
                }
                Bob=player("Bob Marley", 36)
                John=player("John Harris", 66)
                Print Bob.ToString$()
                Print "is to old:"+Bob.IsOld$()
                Print John.ToString$()
                Print "is to old:"+John.IsOld$()
                Dim Players(10)=Player()
                Players(0):=Bob, John
                For i=0 to 1
                        Print Players(i).ToString$()
                Next
                Push Players()
        }
        CheckIt
        Read P()
        For i=0 to 9
                Print P(i).ToString$()
                Print "is to old:"+P(i).IsOld$()
        Next
        A->p(0)
        Print A=>ToString$(), A=>IsOld$()
        A->p(1)
        Print A=>ToString$(), A=>IsOld$()
        Dim A()
        Print A=>ToString$(), A=>IsOld$()
        John=Group(A)
        Print John.ToString$(), John.IsOld$()
        A->0&
        A->John
        Print A=>ToString$(), A=>IsOld$()
        A->0&
        Print type$(A)="Group"
        Push John
}
Big
\\ now stack has a group (not a pointer to group, but a group)
Dim A(10)
\\ we can put it in A(3)
Read A(3)
Print A(3).IsOld$()
k=A(3).Player
PrintK.IsOld$(), "ok"
For A(3).Player {
        Print .IsOld$(), "ok"
}
PrintA(3).Player.IsOld$(), "ok"' call IsOld$ inside Player




Τρίτη 9 Απριλίου 2019

Αντικειμενοστραφής Προγραμματισμός στην Ιστορία της Πληροφορικής (Ι)



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

Το 1962 δημιουργήθηκε το πρόγραμμα Scetchpad, από το καθηγητή του MIT Ivan Sutherland, ηλεκτρολόγο μηχανολόγο, υπό την εποπτεία του καθηγητή Claude Shannon. Σκοπός του προγράμματος ήταν να μπορεί κάποιος να δημιουργεί γραφήματα με γραμμές και τόξα, να ομαδοποιεί κάποια από αυτά και να τα αντιγράφει σε νέες θέσεις πάνω σε έναν νοητό καμβά. Μπορούσε κάποιος να αλλάξει την ομάδα και αυτόματα να αλλάξει και όπου αλλού χρησιμοποιείται. Το πρόγραμμα έτρεχε σε έναν υπολογιστή με 64k μνήμης των 36 bit. Ο σχεδιασμός γίνονταν με ένα lighpen (εφεύρεση του 1955), μια πένα που διαβίβαζε την θέση της στην οθόνη όταν κάποιος έφερνε την πένα πάνω στην οθόνη. Ο μηχανισμός της πένας ήταν ένας συνδυασμός ενός αισθητήρα και ενός προγράμματος. Η στιγμή που ο αισθητήρας έπαιρνε σήμα, καταγράφονταν, όπως και η στιγμή που το πρώτο εικονοστοιχείο εκπέμπονταν, έτσι με τη διαφορά του χρόνου και βάσει του χρονισμού της οθόνης, υπολογίζονταν με ακρίβεια η θέση του εικονοστοιχείου κάτω από την πένα.

Το πρόγραμμα Scetchpad έπρεπε να τρέχει διαδραστικά, δηλαδή την στιγμή της εκτέλεσης να γίνεται και ο χειρισμός με την πένα. Εκείνη την εποχή τα προγράμματα έτρεχαν με το σύστημα της "φουρνιάς" ή batch, δηλαδή γίνονταν εισαγωγή δεδομένων ξεχωριστά από την εκτέλεση του προγράμματος, και κατά την εκτέλεση υπήρχε η εξαγωγή του αποτελέσματος. Ο Sutherland αναγκάστηκε να φτιάξει και δεύτερη έκδοση για επεξεργασία τύπου κατά φουρνιές (batch mode), εκτός από το διαδραστικό (interactive mode). Το πρόγραμμα αποτελούσε την θέση του Sutherland για το διδακτορικό του, με τίτλο "SKETCHPAD, A MAN-MACHINE GRAPHICAL COMMUNICATION SYSTEM", Ιανουάριος 1963. Ο δημιουργός του εστίαζε στο τρόπο επικοινωνίας ανθρώπου και μηχανής. Στην περίληψη στην αρχή της θέσης του, παρουσιάζει την εφαρμογή ως αυτό που περιέχει τα προγράμματα εισαγωγής εξαγωγής και υπολογισμών, όπου δίνεται η δυνατότητα διερμηνείας πληροφορίας όπου άμεσα σχεδιάζεται στην οθόνη του υπολογιστή. Αναφέρει ότι είχε ήδη χρησιμοποιηθεί για σχέδια ηλεκτρολογικά, μηχανικά, επιστημονικά, μαθηματικά, και κινούμενου σχεδίου, συνεπώς όπως το έθετε ήταν γενικής χρήσεως σύστημα. Επιγραμματικά μας λέει ότι με την βοήθεια του προγράμματος μπορούσε κανείς να μετακινήσει σύμβολα (μικρότερα σχέδια μέσα στο σχέδιο), με την δυνατότητα να τραβάει μαζί και συνδέσεις με άλλα σύμβολα. Με αυτό το τρόπο μπορούσε κανείς να φτιάχνει την εισαγωγή σχεδίου για έναν εξομοιωτή κυκλωμάτων (circuit simulator). Για να φτιάξει κανείς μια γραμμή απαιτούνταν ελάχιστους χειρισμούς, δηλαδή να δείξει το πρώτο σημείο, το δεύτερο και με το πάτημα ενός κουμπιού να δει τη γραμμή ακριβώς εκεί που θέλει. Το ενδιαφέρον δεν ήταν μόνο να εμφανίζεται το σχέδιο στην οθόνη, αλλά τα δεδομένα που συστήνουν το γράφημα να μπορούν να αξιοποιηθούν από άλλα προγράμματα, τα οποία θα μοιράζονταν αυτό που ο ίδιος ονόμαζε δομή ring structure. Είναι σημαντικό εδώ να αντιληφθεί ο αναγνώστης ότι όταν μια νέα τεχνολογία μπαίνει στο προσκήνιο τότε εμφανίζονται νέες έννοιες και παίρνουν ονόματα όπως θέλει ο σχεδιαστής της. Όπως γράφει στις ευχαριστίες, πριν την ανάπτυξη της θέσης του, βοηθήθηκε από ξεχωριστές ομάδες CAD, που ασχολούνταν με την μέσω υπολογιστή σχεδίαση. Επίσης ευχαριστεί για την υποστήριξη που είχε με την παροχή χρήσης του υπολογιστή (δεν υπήρχαν τότε οικιακοί υπολογιστές). Επίσης δεν υπήρχαν τα σημερινού τύπου πληκτρολόγια, και τα πλήκτρα και περιστροφικοί διακόπτες ήταν κάποια στην οθόνη παρακείμενα και άλλα σε ξεχωριστή κονσόλα χειριστηρίων. Η οθόνη σχεδιασμού ήταν 7Χ7 ίντσες (1024Χ1024 σημεία). Η εξαγωγή των σχεδίων μπορούσε να γίνει σε plotter, σχεδιαστή πενών, είτε απευθείας από το πρόγραμμα ή με χρήση διάτρητων καρτών!

Διαβάζοντας τη θέση του Sutherland, καθώς περιγράφει το τρόπο χειρισμού του Scetchpad, βλέπουμε την αναφορά στο subpicture, κάνοντας μια αρχική εικόνα ενός εξάγωνου, αποθηκεύοντας την, και σε νέο σχέδιο εφαρμόζοντας τα εξάγωνα σε διάφορες θέσεις, σαν αντικείμενα σχεδίασης. Το σχέδιο με τα πολλά εξάγωνα μπορούσε να αποθηκευτεί και να γίνει έναν νέο subpicture για άλλο σχέδιο. Μας επεξηγεί λοιπόν τα βασικά "αντικείμενα" (δεν τα λέει αντικείμενα, αλλά δυνατότητες του συστήματος - capabilities of the system): subpicture, constraint, definition copying.

Το πρώτο "αντικείμενο" έχει τα στοιχεία, τόξα και ευθύγραμμα τμήματα, που απαρτίζουν το σχέδιο που βλέπουμε. Θα μπορούσε να ήταν ένα εξάγωνο, ή μια πτέρυγα αεροπλάνου, ή έναν γράμμα, ή ό,τι μπορεί να σχεδιαστεί.

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

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

Μας δίνει ένα παράδειγμα ενός εξαγώνου, που έχει οριστεί να είναι εντός ενός κύκλου. Το πρόγραμμα δεν μας αφήνει να βγάλουμε το εξάγωνο από το κύκλο. Δηλαδή έχουμε πληροφορία για το κάθε σχέδιο και σχέσεις μεταξύ τους. Εδώ αρχίζει να αναφέρεται σε ιδιότητες (properties), όπου μια γραμμή μπορεί να είναι με σημεία (dotted line). Επίσης έχουμε την δυνατότητα να αλλάξουμε το σχέδιο μιας subpicture με ένα άλλο και αυτό θα γίνει αυτόματα όπου το χρησιμοποιούμε. Αυτό το χαρακτηριστικό δηλώνει και κάτι άλλο. Ότι στο αρχείο μας δεν έχουμε τυχαίες γραμμές αλλά ξεκάθαρα αντικείμενα. Έτσι το αρχείο μπορεί να χρησιμοποιηθεί όχι μόνο για να εμφανίζεται αλλά για να κρατάει σχέσεις μεταξύ τους, για άλλη χρήση. Σε ένα ηλεκτρικό κύκλωμα, τα στοιχεία του, που τα είδαμε ως subpictures, είναι διακριτά στοιχεία με ιδιότητες που μπορούν να δώσουν υπολογισμούς, ή να χρησιμοποιηθούν για εξομοιώσεις "πραγματικών αντικειμένων".

Συνοψίζοντας ο Sutherland πάνω στη χρήση του προγράμματος, αναφέρει τέσσερα ιδιαίτερα χαρακτηριστικά:
  1. Μικρές αλλαγές σε υπάρχοντα σχέδια. Μια μικρή αλλαγή σε ένα subpicture φαίνεται ως μεγάλη αλλαγή σε σχέδιο που το χρησιμοποιεί σε πολλά σημεία. Για την εποχή εκείνη, όπου το σχέδιο γράφονταν σε μαγνητική ταινία, αυτό σήμαινε μικρό χρόνο για την αλλαγή (δεν άλλαζε όλο το αρχείο).
  2. Κέρδος επιστημονικό ή μηχανολογικό από την κατανόηση των χειρισμών που μπορούν να περιγραφούν γραφικά. Μετακινώντας μέρη του σχεδίου, κάποιες γραμμές "σύνδεσης" αλλάζουν μήκος και θέση ανάλογα με το τύπο τους (constrain) και δίνουν οπτικά την αλλαγή. Σε αυτό συμβάλλει η δυνατότητα να βάλουμε "δηλώσεις" περί των τρόπων αλλαγών.
  3. Εισαγωγή τοπογραφικών δεδομένων, δηλαδή δεδομένα που συνδέονται γραφικά μεταξύ τους, για να περαστούν σε προγράμματα εξομοίωσης.
  4. Σχεδιασμό αναδρομικών σχεδίων. Ένα σχέδιο το οποίο αναπαράγεται εσωτερικά στο ίδιο σχέδιο πολλές φορές. (Χρησιμοποιήθηκε στην κατασκευή μνημών)


Αξίζει εδώ να αναφερθεί και ο τρόπος που ο Sutherland μπήκε στην ιδέα του να φτιάξει το Sketchpad. (το περιγράφει στο κεφάλαιο ΙΙ της θέσης του). Το 1960 ο Jack I. Raffel του λέει ότι υπάρχει μεγάλο ενδιαφέρον στο εργαστήριο Lincoln στο να γίνει ο υπολογιστής περισσότερο προσβάσιμος με την χρήση των οθονών (displays). Κατά το διάστημα 1960-1961, καταπιάνεται πάνω στην οθόνη και την πένα (lightpen), και συνειδητοποιεί ότι αυτά θα είναι χρήσιμα για παραγωγή σχεδίων. Οπότε προσεγγίζει τον Wesley A. Clark, της ομάδας 51 στο Lincoln Laboratory, με την πρόταση να χρησιμοποιήσει έναν TX-2 υπολογιστή για την έρευνα πάνω σε τεχνικές σχεδίασης σε υπολογιστή. Πραγματικά ο Clark είχε βάλει πολλά χαρακτηριστικά σχεδίασης στον TX-2, στα οποία δεν είχε ιδέα κατά την προσέγγιση, αλλά ταίριαζαν σε αυτό που ήθελε. Κατά το φθινόπωρο το 1961, δέχεται ο καθηγητής Claude E. Shannon (θεωρείται ο πατέρας της θεωρίας της πληροφορικής), να τον επιβλέψει, σε αυτό που ήθελε ως διδακτορική θέση. Από εκείνο το σημείο κατάλαβε ότι δεν ήταν ο μόνος που επιδίωκε τους ίδιους σκοπούς, πολλά άτομα στο ΜΙΤ συζητούσαν πάνω σε αυτό. Υπήρχε ήδη το APT, (Automatically Programmed Tool) με το οποίο ένας υπολογιστής παρήγαγε μεταλλικά εξαρτήματα χρησιμοποιώντας χρήσιμες γεωμετρικές τεχνικές χειρισμού. Διαπίστωσε όμως ότι ελάχιστα δείγματα εργασίας υπήρχαν πάνω στο σχεδιασμό γραμμών, ειδικά όταν υπήρχαν ήδη οθόνες και πένες (lightpen). Έτσι φάνηκε άμεσα ότι είχε μοναδική ευκαιρία να κυνηγήσει το ενδιαφέρον του. Ο υπολογιστής ΤΧ-2, ένας πειραματικός μηχανισμός, είχε την δυνατότητα να ανταποκριθεί σε αυτά που ήθελε, καθώς μπορούσε να τροποποιηθεί ανάλογα με τις ανάγκες. Όπως δηλώνει, όταν έμαθε τι είναι η σχεδίαση σε έναν υπολογιστή μπορούσαν και μικρότερες μηχανές να δουλέψουν για πρακτικά ζητήματα.

Τα πρώτα ζητήματα που έλυσε ήταν το πώς θα δουλεύει με την πένα (lightpen), γράφοντας το πρόγραμμα για την ανίχνευσή της (tracking) καθώς και πώς θα εμφανίζει όχι μόνο γραμμές αλλά και μέρη  ενός μεγαλύτερου σχεδίου στην οθόνη. Κατά τον Δεκέμβριο του 1961 τον επισκέφθηκε ο καθηγητής Sannon, και του υπέδειξε να βάλει τον κύκλο (μέχρι τότε μόνο με γραμμές είχε ασχοληθεί). Δούλεψε το κύκλο, με δύσκολο θέμα το πώς να γίνεται η ανίχνευση ακμής, edge detection, σε αυτόν), στο πρώτο πρόγραμμα. Μετά πέρασε σε ένα δεύτερο, όπου εφάρμοσε διασταυρώσεις (intersections) μεταξύ γραμμών και κύκλων. Στην θέση του, για το ντοκτορά, περιγράφει τις δυσκολίες και τα λογικά λάθη που υπέπεσε, για να καταλήξει σε ένα σωστό πρόγραμμα, μετά από πολλές δοκιμές. Το πρόγραμμα στη στιγμή εκείνη έκανε ελάχιστα πράγματα, σε σχέση με το τελικό πρόγραμμα. Έβγαλε μια ταινία που έδειχνε τη χρήση του (δεν υπήρχαν βιντεοκάμερες) και φωτογραφίες που τις έδειχνε σε διάφορα άλλα άτομα για να πάρει ιδέες και να προσθέσει δυνατότητες. Από εκεί βγήκε η ιδέα ότι μια γενική δομή θα ήταν καλύτερη για να μπορεί το πρόγραμμα να επεκταθεί. Τον Ιούνιο του 1962, όλη η νέα πληροφόρηση που άντλησε έδωσε την αρχή ενός εντελώς νέου συστήματος όπου με λίγες αλλαγές έγινε αυτό που λέμε Scetchpad.

Στο τελικό πρόγραμμα επιλέχθηκε μια γενική δομή, διαχωρισμός των υποπρογραμμάτων (subroutines), σε γενικού σκοπού που εφαρμόζονται σε όλους τους τύπους μερών σχεδίων, και σε εκείνους που εφαρμόζονται σε ξεχωριστούς τύπους, καθώς και απεριόριστη χρήση συναρτήσεων. (Την εποχή εκείνη δεν υπήρχαν προγραμματιστές, ο προγραμματισμός ήταν επικουρικός άλλων επιστημών). Το καλοκαίρι του 1962, το πρόγραμμα έδειχνε αξιοσημείωτη δύναμη. Ο Leonard M. Hantman κοίταξε το πρόγραμμα από θέμα σφαλμάτων, και πρόσθεσε συναρτήσεις για χρήση μαγνητικών ταινιών, και σύνδεση με το plotter (αυτόματο σχεδιαστή με πένες). Με το καιρό λιγότερη αποσφαλμάτωση γίνονταν, και περισσότερες εφαρμογές του προγράμματος πραγματοποιούνταν. Φτιάχτηκε εγχειρίδιο χρήσης οδηγιών και μια ομάδα που χρησιμοποιούσε το πρόγραμμα για εργασίες τρίτων. Δηλαδή το πρόγραμμα απέκτησε χρήστες. Από την δουλειά αυτών προέκυψε και το αλφάβητο που χρησιμοποιεί το Scetchpad για να εμφανίζει γράμματα στα σχέδια. Σχέδια για γέφυρες δεν ήταν αναμενόμενες, ως δουλειά του Scetchpad.

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

Η οθόνη του υπολογιστή που χρησιμοποιούταν τότε, δεν δούλευε όπως οι σημερινές οθόνες. Έπαιρνε πρόγραμμα που οδηγούσε την εκπομπή ηλεκτρονίων στο σημείο της οθόνης που θέλαμε, με απόδοση εκατό χιλιάδων σημείων το δευτερόλεπτο. Θα μπορούσαμε να το δούμε ως χίλια ευθύγραμμα τμήματα των εκατό σημείων. Οι μικροϋπολογιστές της δεκαετίας του 80, είχαν 320Χ256 σημεία, τα οποία τα πρόβαλλαν σε δυο πλαίσια των 320Χ128 σημείων 50 φορές το δευτερόλεπτο, με χρήση οκτώ χρωμάτων (με το άσπρο και το μαύρο σε αυτά). Η διαφορά βέβαια ήταν στο παλιό σύστημα ότι ο φώσφορος αργούσε να σβήσει αφού δεν ήταν ο σκοπός να δείξει 50 πλαίσια το δευτερόλεπτο, αλλά έναν αριθμό σημείων το δευτερόλεπτο. Για να δουλέψει το lightpen, έπρεπε να εμφανίζεται ένας σταυρός και την ώρα της εμφάνισης να γίνεται έλεγχος του αισθητήρα της πένας. Αυτό το έκανε το πρόγραμμα που έδειχνε τα σημεία στην οθόνη. Στην αρχική έκδοση το πρόγραμμα διάβαζε την ίδια δομή που δημιουργούσαμε για το σχέδιο και είχε προβλήματα, όταν σβήναμε ή αλλάζαμε ένα στοιχείο ενώ εκείνη τη στιγμή το στοιχείο προβαλλόταν! Για το λόγο αυτό φτιάχτηκε ξεχωριστός πίνακας σημείων (spots). Επίσης για λόγους συγχρονισμού μπήκε ένας διακόπτης που άλλαζε το τρόπο προβολής από κανονικός σε διαχωριζόμενο (interlace). Ο διαχωρισμός έκανε την εμφάνιση των στοιχείων να γίνεται ανά οκτώ σημεία, με συνέπεια σε ορισμένα σχέδια να φαίνεται ότι το σχέδιο χορεύει, και έτσι να απαιτείται η αλλαγή προβολής! Κάθε σημείο γράφονταν στην μνήμη των 36bit, σε μια θέση, ώστε τα 20bit να δηλώνουν στην θέση στην οθόνη και τα υπόλοιπα 16 να δηλώνουν σε ποιο στοιχείο στην μνήμη των στοιχείων αφορά. Έτσι όταν σημαδεύουμε ένα σημείο με την πένα να βρίσκει το πρόγραμμα άμεσα το στοιχείο.


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


Κάποτε ρώτησαν τον Sutherland, "Πώς είναι δυνατόν να έχει κάποιος φτιάξει το πρώτο διαδραστικό πρόγραμμα γραφικών, την πρώτη μη διαδικαστική γλώσσα προγραμματισμού, τη πρώτη αντικειμενοστραφή εφαρμογή, και όλα αυτά σε έναν χρόνο". Η απάντηση ήταν "Λοιπόν, δεν ήξερα ότι ήταν δύσκολο!"

Πέμπτη 4 Απριλίου 2019

Chain of Responsibility design pattern

Today I have a OOP design pattern, the Chain of Responsibility. There are two programs, one use weak references (pointers to named groups), so we have three loggers connected, and the second use real pointers. The two examples report the same messages:


Writing to console: Entering function ProcessOrder().
Writing to console: Order record retrieved.
Writing to console: Customer Address details missing in Branch DataBase.
Writing to Log File: Customer Address details missing in Branch DataBase.
Writing to console: Customer Address details missing in Organization DataBase.
Writing to Log File: Customer Address details missing in Organization DataBase.
Writing to console: Unable to Process Order ORD1 Dated D1 For Customer C1.
Sending via email: Unable to Process Order ORD1 Dated D1 For Customer C1.
Writing to console: Order Dispatched.
Sending via email: Order Dispatched.


You can open the M2000 Interpreter, and write to console Edit A then press Enter and paste the code of the first example, then press Esc and write A and press Enter to execute it. Then do the same for B module, and last use Save chainof to save it as chainof.gsb for using it later (start M2000 Interpreter and write Load chainof to get it back).

Explain of constructor of ConsoleLogger. We have to use the same name as class name for a module. It is a module but is called as a function ConsoleLogger(). All arguments are in stack of values so because we want to extend this class we have to call Logger() passing the current stack. The [] identifier get the pointer of stack of values (is an object) and leave an empty stack. The ! before a stack object empty the stack by merging all values to callee function Logger(). So with this trick we pass the arguments to Logger() without using variables. The returned group is a float group and this merged to This object. Because we use Module Final WriteMessage {} no merging done for this module.
Module ConsoleLogger {
This=Logger(![])
}




\\ Chain of Responsibility design pattern 
\\ a class: part in Class Function removed from object after first copy (when copied from Class function)
\\ So constructors are not part of Logger Group (Group is the type of a user object in M2000)
Form 80,50
Global enum LogLevel    {
                    None = 0,
                    Info = 1, 
                    Debug = 2, 
                    Warning = 4,
                    Error = 8,   
                    FunctionalMessage = 16, 
                    FunctionalError = 32, 
                    All = 63              
             }
Class Logger {
Private:
 logMask=0&
 Group NextLogger
Public:
 Module Final Message(msg$, severity) {
  if not Valid(.NextLogger=>Null) then
   .NextLogger=>Message msg$, severity
  end if
  if Binary.And(.logMask ,severity)<>0 then
   .WriteMessage msg$
  End if
 }
 Module WriteMessage (m$) {
  Print m$
 }
 Module Final SetNext ( p as pointer) {
  .NextLogger<=p
 }
 Function Final Dispose {
  x=valid(.NextLogger=>Dispose())
  Clear .NextLogger
 }
Class:
 Module Logger (z) {
  .logMask<=z
  class EmptyLogger {
   Null
  }
  .NextLogger<=Pointer(EmptyLogger())
 }
}
Class ConsoleLogger {
 Module  Final WriteMessage (m$) {
  Print "Writing to console: " + m$
 }
class:
 Module ConsoleLogger {
  This=Logger(![])
 }
}
Class EmailLogger {
 Module  Final WriteMessage (m$) {
  Print "Sending via email: " + m$
 }
class:
 Module EmailLogger {
  This=Logger(![])
 }
}
Class FileLogger {
 Module  Final WriteMessage (m$) {
  Print "Writing to Log File: " + m$
 }
class:
 Module FileLogger {
  This=Logger(![])
 }
}
A=ConsoleLogger(All)
B=EmailLogger(FunctionalMessage+FunctionalError)
C=FileLogger(Warning+Error)
C.SetNext Pointer(B)
B.SetNext Pointer(A)


C.Message "Entering function ProcessOrder().", Debug
C.Message "Order record retrieved.", Info
C.Message "Customer Address details missing in Branch DataBase.", Warning
C.Message "Customer Address details missing in Organization DataBase.", Error
C.Message "Unable to Process Order ORD1 Dated D1 For Customer C1.", FunctionalError
C.Message "Order Dispatched.",FunctionalMessage
Call C.Dispose()  ' no need we use weak refrences (automatic created if poiner produced for named groups)







Write Edit B in M2000 console press enter and copy this, then press Esc to exit to console.

\\ Chain of Responsibility design pattern  2
\\ example from https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern
\\ This example use real pointers to groups
\\ so we have one group, the logger and two groups inside it in a chain
Form 80,50
Global enum LogLevel    {
                    None = 0,
                    Info = 1, 
                    Debug = 2, 
                    Warning = 4,
                    Error = 8,   
                    FunctionalMessage = 16, 
                    FunctionalError = 32, 
                    All = 63              
             }
Class Logger {
Private:
 logMask=0&
 Group NextLogger
Public:
 Module Final Message(msg$, severity) {
  if Binary.And(.logMask ,severity)<>0 then
   .WriteMessage msg$
  End if
  if not Valid(.NextLogger=>Null) then
   .NextLogger=>Message msg$, severity
  end if
 }
 Module WriteMessage (m$) {
  Print m$
 }
 Module Final SetNext ( p as pointer) {
  if  valid(.NextLogger=>Null) then
   .NextLogger<=p
  else
   .NextLogger=>SetNext p
  end if
 }
 Function Final Dispose {
  x=valid(.NextLogger=>Dispose())
   Clear .NextLlogger
 }
Class:
 Module Logger (z) {
  .logMask<=z
  class EmptyLogger {
   Null
  }
  .NextLogger<=Pointer(EmptyLogger())
 }
}
Class ConsoleLogger {
 Module  Final WriteMessage (m$) {
  Print "Writing to console: " + m$
 }
class:
 Module ConsoleLogger {
  This=Logger(![])    '  ![] pass current stack of values to function Logger()
 }
}
Class EmailLogger {
 Module  Final WriteMessage (m$) {
  Print "Sending via email: " + m$
 }
class:
 Module EmailLogger {
  This=Logger(![])
 }
}
Class FileLogger {
 Module  Final WriteMessage (m$) {
  Print "Writing to Log File: " + m$
 }
class:
 Module FileLogger {
  This=Logger(![])
 }
}
Logger->ConsoleLogger(All)
Logger=>SetNext Pointer(EmailLogger(FunctionalMessage+FunctionalError)) Logger=>SetNext Pointer(FileLogger(Warning+Error))
Logger=>Message "Entering function ProcessOrder().", Debug \\ better to use a For object {} structure For Logger { .Message "Order record retrieved.", Info .Message "Customer Address details missing in Branch DataBase.", Warning .Message "Customer Address details missing in Organization DataBase.", Error .Message "Unable to Process Order ORD1 Dated D1 For Customer C1.", FunctionalError \\ we can use Logger=>Message also Logger=>Message "Order Dispatched.",FunctionalMessage call .Dispose() } Logger->0& ' or Clear Logger

Παρασκευή 29 Μαρτίου 2019

Revision 14 Version 9.8

Some bug fixes.
1) Fix for passing array items by reference. Before works if we make a call from a module. The problem was the call from a function.
This work on previous revision 13
module b {
 dim a(10)=-1
 inc=lambda ->{
  stack
  read &x
  x++
 }
 call inc(&a(3))
 Print a(3)
}
call b
b

but not this (work nice in revision 14):
function b {
 dim a(10)=-5
 inc=lambda ->{
  stack
  read &x
  ? x
  x++
 }
 call inc(&a(3))
 Print a(3)
}
call b()
n=b()
Also the above programs show the stack (as we call) before the reading (and linking to a variable). An Array item passed by reference with a copy in copy out method. First a temporary variable hold a copy of array item, then the reference of that variable send to stack, so the read &x make x as a link to temporary variable (so x has no own data, it is an alias of the temporary variable). Also for this call interpreter save to a special collection the data which need to make the copy out at the end of the call.
The stack statement, inside function, show the strings (as references) of the temporary variables which interpreter produce. If array for some reason lost the item which we have to copy out, no error produced, the interpreter just do nothing for store back the value from temporary variable.

global z=2
function k (&n, &x) {
 x++
 n++
 Print x
}
dim a(3,3)
a(z,1)=100
call k(&z, &a(z,1))
Print a(2,1)=101, z=3
z<=2   ' need <= to assign a value to a global, otherwise we get a local one
\\ change the k (also change the signature too)
function k  {
 z++
 read &x
 x++
 Print x
}
\\ indexes calculated before the call
call k(&a(z,1))
Print a(2,1)=102, z=3


2) Another fix is in Each() function which return an iterator object. There are two forms, one using comma and another without, using a descriptive text.

Here is the program which make for Rosettacode.org (Four bit adder), which found the errors, and immediately fix them.


ha means half adder, fa is a full adder. We simulate bits using just values 0 and 1 (these are doubles, the default type for numbers). For this reason we don't use And operator or Binary.And() for binary And, and the other gates, but we have three arrays, as functions, for not(), and(), or(). Also task want to have a function for xor, so xor isn't an array.
Then we make lambda with closures whatever need each one. Lambda add4 need fa as a closure.
The add4 may work from one or more bits (so name is not exactly a match for this implementation)
We have a lambda called source which get a number of values, in a private stack, then reverse the order and return an array of these values. We do that to send the left most value as the last item of array. The output just written in the res()  array, which we pass by reference (this is not a copy in-copy out process, but a simple alias, the two names. res() and out() are local on different scopes, on same data). So to print the result first we have to print the carry, and next the top value res(3) until the res(0). So we make k as an iterator (which hold a pointer to array res(), plus other data, and the cursor, which we can access using k^), which work in a While {} or in a Print statement. Here in a print statement we get all values each on one column, and may wrap to next line if no column left in line.

\\ when we use Call we pass the same stack in the call, so we need to clear the stack before
\\ or we can use a block for a new stack: Stack New  {}
\\ when the block exit the old stack return as current stack.
\\ so here Flush empty the current stack
Flush
dim not(0 to 1),and(0 to 1, 0 to 1),or(0 to 1, 0 to 1)
not(0)=1,0
and(0,0)=0,0,0,1
or(0,0)=0,1,1,1
xor=lambda not(),and(),or() (a,b)->or(and(a,not(b)), and(b, not(a)))
ha=lambda xor, and() (a,b, &s, &c)->{
 s=xor(a,b)
 c=and(a,b)
}
fa=lambda ha, or() (a,b,c0, &s,&c1)->{
 def sa,ca,cb
 call ha(a,c0,&sa,&ca)
 call ha(sa, b, &s,&cb)
 c1=or(ca,cb)
}
add4=lambda fa (inpA(),inpB(),&v, &out()) ->{
 dim carry(0 to len(InpA()))=0
 for i=0 to len(InpA())-1
  mm=fa(InpA(i),inpB(i),carry(i),&out(i),&carry(i+1))
 next
 v=carry(i)
}
dim res(0 to 3)=-1
source=lambda->{
 shift 1, -stack.size  ' reverse stack items
 =array([])  ' convert current stack to array, empty current stack
}
def v
call add4(source(1,0,1,0),source(1,0,0,1),&v, &res())
'k=each(res(), -1, 1) ' another way to do the same as the later
k=each(res() end to start)  ' k is an iterator, now configure to read items in reverse
Print v, k

3) Fix at EditBox when it is in a M2000 mode (for displaying M2000 code), for the F2 and F3 when we mark an identifier (any type), works nice (before a flag make the F2 and F3  to work for words only). This problem not exist if we pass a list of identifiers for syntax color.

4) Added Hex statement in Help File.(there is a Hex$() but for no reason I never make a record for Hex statement, a special Print for results in Hexadecimal)