## Σάββατο, 29 Αυγούστου 2020

### Πρόγραμμα Εκμάθησης Προγραμματισμού: Μοίρασε τα Μήλα

Η άσκηση:

 Έχουμε 10 παιδιά σε έναν κύκλο, και θέλουμε να μοιράσουμε ένα καλάθι μήλα. Το καλάθι μπορεί να έχει από 7 έως 50 μήλα. Δίνουμε σε ένα οποιοδήποτε παιδί το καλάθι. Κάθε παιδί που παίρνει το καλάθι κρατάει ένα μήλο και δίνει το καλάθι στο επόμενο παιδί με μια φορά για όλα τα παιδιά. Μόλις τελειώσουν τα μήλα από το καλάθι πρέπει να μετρήσουμε πόσα μήλα πήραν τα παιδιά φτιάχνοντας μια ή δυο ομάδες. Μια ομάδα αν έχουν μοιραστεί δίκαια τα μήλα, δηλαδή κάθε παιδί πήρε ίδιο αριθμό μήλων, ή αν μερικά παιδά δεν πήραν καθόλου μήλα. Δυο ομάδες αν έχουν μοιραστεί σε μερικά παιδιά που πήραν λιγότερα μήλα από άλλα. Θα εμφανίζουμε στην αρχή ένα λεκτικό "Υπολογισμός" στο κέντρο της γραμμής της κονσόλας της Μ2000. Πρέπει να δείξουμε ένα πίνακα που θα λέει για κάθε ομάδα πόσα παιδιά πήραν πόσα μήλα και συνολικά πόσα μήλα πήραν. Ως επικεφαλίδες ο πίνακας θα έχει κενό “”, “Παιδιά”, “Μήλα”, “Σύνολο”. Σε κάθε γραμμή του πίνακα θα εμφανίζουμε τα “Ομάδα1:” και “Ομάδα2:” (αν έχουμε παιδιά στην Ομάδα 2 αν όχι δεν θα εμφανίζουμε τη γραμμή αυτή). Θα βρούμε και θα δείξουμε το σύνολο των μήλων στο καλάθι, από τα υπολογιζόμενα και θα δείξουμε αν επαληθεύεται το νούμερο με το αρχικό νούμερο του καλαθιού με το λεκτικό “Επαλήθευση:Σωστή”. Τέλος αν έχουμε δίκαιη μοιρασιά θα το δείξουμε και αυτό με εμφάνιση του λεκτικού “Δίκαιη Μοιρασιά”.

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

Για το καλάθι θα χρησιμοποιήσουμε την Τυχαίος(7, 50) που δίνει έναν ακέραιο αριθμό από 7 έως 50.

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

Για να προχωράμε το καλάθι στο επόμενο παιδί θα αυξάνουμε μια μεταβλητή Παιδί που χρησιμοποιούμε ως δείκτη στον πίνακα Μήλα() και αν η αύξηση γίνει μεγαλύτερη του 10 τότε θα βάζουμε την τιμή 1 για να γίνεται ο κύκλος όπως λέει η άσκηση.

Χρησιμοποιούμε μεταβλητές τύπου Διπλός (δηλαδή πραγματικός αν το δούμε στη ΓΛΩΣΣΑ του σχολείου).

Χρησιμοποιούμε για την εμφάνιση στη κονσόλα της Μ2000 την \$(Κέντρο) που αλλάζει την εμφάνιση στην στήλη με οριζόντια στοίχιση στο κέντρο της στήλης. Η Τύπωσε Πάνω αφενός καθαρίζει όλη την γραμμή, και αφετέρου την χρησιμοποιεί ως μια στήλη. Έτσι η \$(Κέντρο) βάζει το λεκτικό "Υπολογισμός". Επειδή η Τύπωσε Πάνω όπως και η Τύπωσε Μέρος δεν αλλάζουν γραμμή, θα βάζουμε μια Τύπωσε για αλλαγή γραμμής.

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

Μετά με μια επανάληψη για κάθε παιδί, βρίσκουμε το μικρότερο και το μεγαλύτερο στο πίνακα Μήλα().

Υπάρχουν τρεις περιπτώσεις.

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

Στην περίπτωση 2, θα χρησιμοποιήσουμε την πρώτη ομάδα, έτσι θα αντιγράψουμε το μεγαλύτερο στο μικρότερο. Επειδή το Ομάδα2 θα έχει τιμή 0, στο λογαριασμό που κάνουμε το δεύτερο μέρος θα είναι μηδέν: Ομάδα1*Μικρότερο+Ομάδα2*Μεγαλύτερο

Επειδή στη περίπτωση 2 βάζουμε τη τιμή του Μεγαλύτερο στο Μικρότερο, έχουμε βάλει την συνθήκη Μικρότερο=Μεγαλύτερο στην ΔίκαιηΜοιρασιά, ώστε να κρατήσουμε τη πραγματική τιμή της συνθήκης για να την χρησιμοποιήσουμε στο τέλος, για την εμφάνιση ή μη του λεκτικού "Δίκαιη Μοιρασιά"

Η περίπτωση 2 συμβαίνει όταν έχουμε Καλάθι με λιγότερα μήλα από 10.

Για την περίπτωση 3 το να δώσουμε πάλι το Μικρότερο=Μεγαλύτερο δεν μας χαλάει (αφού είναι ήδη ίσα). Η περίπτωση 3 συμβαίνει όταν έχουμε Καλάθι πολλαπλάσιο του 10 (από το 10).

Η περίπτωση 1 είναι η πιο συχνή περίπτωση όπου η ομάδα 1 έχει παιδιά με ένα μήλο λιγότερο από αυτά της ομάδας 2.

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

Κρατάμε την ΚαλάθιΑρχικό για να κάνουμε την επαλήθευση.

 Τμήμα ΜοίρασεΜήλα {       Σταθερη Κέντρο=2       Πίνακας Μήλα(1 έως 10)       Μικρότερο=Απειρο       Μεγαλύτερο=0       Ομάδα1=0       Ομάδα2=0       Καλάθι=Τυχαίος(7, 50)       ΚαλάθιΑρχικό=Καλάθι       Παιδί=Τυχαίος(1, 10)       ΔίκαιηΜοιρασιά=Ψευδές       Ενώ Καλάθι>0             Μήλα(Παιδί)++ ' δίνω ένα μήλο             Καλάθι-- ' αφαιρώ από το καλάθι             Παιδί++ ' επόμενο παιδί             Αν Παιδί>10 Τότε Παιδί=1 ' γυρνάμε στο πρώτο       Τέλος Ενώ       Τύπωσε Πάνω \$(Κέντρο), "Υπολογισμός"       Τύπωσε       Για ι=1 έως 10             Αν Μήλα(ι)<Μικρότερο Τότε Μικρότερο=Μήλα(ι)             Αν Μήλα(ι)>Μεγαλύτερο Τότε Μεγαλύτερο=Μήλα(ι)       Επόμενο       ΔίκαιηΜοιρασιά=Μικρότερο=Μεγαλύτερο       Αν Μικρότερο=Μεγαλύτερο ή Μικρότερο=0 Τότε             Για ι=1 έως 10                   Αν Μήλα(ι)=Μεγαλύτερο Τότε Ομάδα1++             Επόμενο             Μικρότερο=Μεγαλύτερο       Αλλιώς             Για ι=1 έως 10                   Αν Μήλα(ι)=Μικρότερο Τότε Ομάδα1++                   Αν Μήλα(ι)=Μεγαλύτερο Τότε Ομάδα2++             Επόμενο       Τέλος Αν       Τύπωσε Μέρος \$(Κέντρο),"","Παιδιά", "Μήλα", "Σύνολο"       Τύπωσε       Τύπωσε "Ομάδα 1:", Ομάδα1, Μικρότερο, Ομάδα1*Μικρότερο       Αν Ομάδα2<>0 Τότε             Τύπωσε "Ομάδα 2:",Ομάδα2, Μεγαλύτερο, Ομάδα2*Μεγαλύτερο       Τέλος Αν       Τύπωσε "Καλάθι:";Ομάδα1*Μικρότερο+Ομάδα2*Μεγαλύτερο       Αν Ομάδα1*Μικρότερο+Ομάδα2*Μεγαλύτερο=ΚαλάθιΑρχικό Τότε             Τύπωσε "Επαλύθευση:Σωστό"       Τέλος Αν       Αν ΔίκαιηΜοιρασιά Τότε Τύπωσε "Δίκαιη Μοιρασιά" } ΜοίρασεΜήλα

Γιώργος Καρράς

Υ.Γ.

Η άσκηση αυτή μπορεί να αντιγραφεί σε οποιαδήποτε γλώσσα από οποιονδήποτε, ελεύθερα!

### OOP Programming M2000, Word Wrapping

Reading an article “Simulating Coroutines in Unmanaged C++”, I got the idea for this article. No we don’t use here Coroutines. We get only the example which tell for the text in the following frame need to formatted at screen, or any other output, to 44 characters as line length, using wrapping for words and excluding double or more spaces. The text supposed to be inserted from a file.

The Input File:

 ```44 He [Sarek] was delighted to discover how very much like him they [computer people] were ... All they cared about was the art of their work, and doing it right. It was hard not to admire such dedication and love of computing for its own sake. The programmers were the first Earth people he came to understand as being really human. Diane Duane.- "Spock's World."```

The Output:

 ```He [Sarek] was delighted to discover how very much like him they [computer people] were ... All they cared about was the art of their work, and doing it right. It was hard not to admire such dedication and love of computing for its own sake. The programmers were the first Earth people he came to understand as being really human. Diane Duane.- "Spock's World."```

Our first solution has to be easy to implement. So we say that the input file is a string variable holding the text. Also we have to say that the output has to be performed to screen. The M2000 console can display text at a proportional or non proportional manner on character width. By default we use the non proportional (like in cmd.exe). We can use a FORM statement to make the line width above 44 characters, say FORM 60, 40 is good for this.

The First Program:

 Module FirstProgram {       a\$={44       He [Sarek] was      delighted to discover   how very much like       him they    [computer people] were   ...  All they       cared about was the art of their       work, and doing            it right.  It was hard not       to admire such dedication and love of       computing for      its own   sake.       The programmers were the   first       Earth    people  he came   to   understand as being       really human.       Diane Duane.- "Spock's World."       }       word=false       buff\$=""       line\$=""       \\ using "??" we get the integer value       T=val(a\$,"??",m) \\ m is the index of first char not included to value       DRule\$=""       for i=0 to T div 10             DRule\$+=If\$(i=0->string\$(" ",10),string\$(str\$(i,""),10))       next       DRule\$=Mid\$(DRule\$,2, T)       Rule\$=string\$("1234567890", T div 10)+Mid\$("1234567890",1, T mod 10)       Print DRule\$       Print Rule\$       for i=m to len(a\$)             if chrcode(mid\$(a\$, i, 1))>32 then                   if word then                         Gosub SimpleSub                         word=false                         buff\$=mid\$(a\$,i,1)                   else                         buff\$+=mid\$(a\$,i,1)                   end if             else                   word=true                                end if       next i       Gosub SimpleSub       If line\$<>"" Then Print line\$       End SimpleSub:             if len(buff\$)>0 then                   if len(line\$)+1+len(buff\$)>T then                         if len(line\$)>0 then Print line\$                         line\$=""                   end if                   if len(line\$)=0 then                         line\$=buff\$                   else                         line\$+=" "+buff\$                   end if             end if             Return } FirstProgram

As we see from the FirstProgram module, we use some variables:

 a\$ String The Input Text word Boolean True means: Search for Word False means: Word founded, look for other characters buff\$ String A buffer for the word line\$ String A buffer for line T Double Hold the length for the proper output DRule\$ String A computed string for first line of ruler Rule\$ String A computed string for second line of ruler m Double A helper which hold the index of not used char in Val(). i Double The index used in Mid\$(a\$, i, 1) to copy a character from a\$

The ruler used for checking the output:

 ```         11111111112222222222333333333344444 12345678901234567890123456789012345678901234```

To check if a character has to stored in the word buffer we have to get the character code and if the code is bigger than 32 then this we say is a character for word. In our text we have 32 for Space, 13 and 10 for newline (two characters for a Crlf, or carriage return and line feed). So using the formula chrcode(mid\$(a\$, i, 1))>32 we check the code in index I on a\$ string.

We need to use a boolean variable Word to determine if we have the first character for a word.

 Charcode > 32 Word Result False False Word=True False True Word=True True False Append character to buff\$ True True First character for a Word / see more

The last row has an additional logic. Before we pass the first character to buff\$ we have to see if the buff\$ has a word. We use Len(buff\$) to check if we have something, so the value is non zero. So if we have a previous word we have to check if we can add to line\$. For easy calculation we say that a word never get the T length or more tan T length. So we have only to check the total length of line, using len(line\$) plus one for a space plus the len\$(buff\$). If this value is more than T then we need to print the line\$ and then make the line\$ as the buff\$ (so this is the word wrapping), so the new character (the First character) now assigned to buff\$, and Word change to False.

When Charcode function return a value bigger than 32 and Word is False then we append character to buff\$. When we get a value from Charcode function of 32 or less we just turn Word to False.

We use a simple subroutine, because the same code used after the scanning of input text (a\$) when we have a buff\$ (a word) of length>0 so we have to put it to current line or print the current line and put it to the last line (as new current one). Simple subroutines in M2000 used like in BASIC, with a label and the Return statement. A simple subroutine execute code like the code is in the place we call it, using the same module name space, without using local variables.

Look how to make the ruler, using the two variables, DRule\$ and Rule\$. We use the String\$() with a string expression and a number expression as parameters. We get a string times number.

Another Try

Because we need something better, we looking for using a Group (an Object) to mimic a file. So now we have a second program, to do that.

The Second Program

 Module SecondProgram {       Group MyFile {       Private:             index=1             a\$={44             He [Sarek] was      delighted to discover   how very much like             him they    [computer people] were   ...  All they             cared about was the art of their             work, and doing            it right.  It was hard not             to admire such dedication and love of             computing for      its own   sake.             The programmers were the   first             Earth    people  he came   to   understand as being             really human.             Diane Duane.- "Spock's World."             }       Public:             \\ = 0=1 return boolean false             Property EOF{value}=0=1             Function GetNumber {                   m=0                   =val(mid\$(.a\$, .index),"??",m)                   .index+=m             }             Function GetWord\$ {                   while chrcode(mid\$(.a\$, .index, 1))<33                         .index++                         if .index>len(.a\$) then .[EOF]<=True: Break                   end while                   m=.index                   while chrcode(mid\$(.a\$, .index, 1))>32                         .index++                         if .index>len(.a\$) then .[EOF]<=True: Exit                   end while                   =mid\$(.a\$, m, .index-m)             }       }       buff\$=""       line\$=""       T=MyFile.GetNumber()       DRule\$=""       for i=0 to T div 10             DRule\$+=If\$(i=0->string\$(" ",10),string\$(str\$(i,""),10))       next       DRule\$=Mid\$(DRule\$,2, T)       Rule\$=string\$("1234567890", T div 10)+Mid\$("1234567890",1, T mod 10)       Print DRule\$       Print Rule\$       Repeat             buff\$=MyFile.GetWord\$()             if MyFile.EOF then exit             if len(line\$)+1+len(buff\$)>T then                   if len(line\$)>0 then Print line\$                   line\$=""             end if             if len(line\$)=0 then                   line\$=buff\$             else                   line\$+=" "+buff\$             end if       Always       If line\$<>"" Then Print line\$ } SecondProgram

Now we have the MyFile group. This group has two private members, the a\$ as the input text and the index member. Also this group has three public members, a property EOF (for End Of File), and two methods, GetNumber() and GetWord\$().

The main code has many items from first program. The subroutine not used here, because the loop, Repeat Always, works fine and has the part of subroutine part of it.

The new code didn’t use a Word boolean variable, because the method GetWord\$() return a word or nothing. Also this function change the hidden variable [EOF] which return by the read only property EOF, and for MyFile is the MyFile.EOF. And the m variable not used in the main code because now exist only in method GetNumber().

How the method GetWord\$() works?

Each time we check a character at index, we add one to index and check if the index is bigger than length of a\$. The first time we check the character code for less than 33. We want to skip all these characters with code less than 33. If we get an EOF we just make a Break (this break the code of method GetWord\$() and return the last value which we place as return value, or the default value, the empty string for a string function. So if we pass the first loop without breaking it we have the first character of a word. We get in a second loop while we find a character code bigger than 32. Here we use Exit because we need to return the word. We didn’t use a buff\$ variable for word, we just get two indexes and then we get a sub string from a\$ using Mid\$().

Looking the two programs we see that the second one has an outer loop which are not connected to index variable (like variable i in first program). The index is hidden in Group object.

So now lets see another version

Third Try

What if we have a second object for rendering the output?

Here we put another public method in MyFile object, the OpeFile, which prepare the index, to value one, an reset the EOF internal (hidden/private) variable [EOF]. Also we make another group, the Render group with three members, the T for the width for rendering, the Ruler() and the Screen() methods. We pass the MyFile object by reference to Render object. We can display or not the ruler, but we have to call Render.Ruler to get the proper T from file in MyFile.

We use Type: File to make the type of group MyFile as File (so we can check when get a group in Ruler and Screen methods). Classes in M2000 always insert a proper type directive, but for groups we define by using Group statement we have to put it if we want it, by using Type: (we can add one or more names)

The code inside Render isn’t new to us.

Third Program

 Module ThirdProgram {       Group MyFile {       Type: File       Private:             index=100000             a\$={44             He [Sarek] was      delighted to discover   how very much like             him they    [computer people] were   ...  All they             cared about was the art of their             work, and doing            it right.  It was hard not             to admire such dedication and love of             computing for      its own   sake.             The programmers were the   first             Earth    people  he came   to   understand as being             really human.             Diane Duane.- "Spock's World."             }       Public:             Property EOF{value}=1=1             Module OpenFile {                   .index<=1                   .[EOF]<=False             }             Function GetNumber {                   m=0                   =val(mid\$(.a\$, .index),"??",m)                   .index+=m             }             Function GetWord\$ {                   while chrcode(mid\$(.a\$, .index, 1))<33                         .index++                         if .index>len(.a\$) then .[EOF]<=True: Break                   end while                   m=.index                   while chrcode(mid\$(.a\$, .index, 1))>32                         .index++                         if .index>len(.a\$) then .[EOF]=True: Exit                   end while                   =mid\$(.a\$, m, .index-m)             }       }       Group Render {             T=0             Module Ruler (&File as File, ToScreen=False){                   .T<=File.GetNumber()                   If .T>89 then Error "Cant handle such width"                   DRule\$=""                   for i=0 to .T div 10                         DRule\$+=If\$(i=0->string\$(" ",10),string\$(str\$(i,""),10))                   next                   If Not ToScreen Then Exit                   Print Mid\$(DRule\$,2, .T)                   Print string\$("1234567890", .T div 10)+Mid\$("1234567890",1, .T mod 10)             }             Module Screen (&File as File) {                   line\$=""                   buff\$=""                   Repeat                         buff\$=File.GetWord\$()                         if File.EOF then exit                         if len(line\$)+1+len(buff\$)>.T then                               if len(line\$)>0 then Print line\$                               line\$=""                         end if                         if len(line\$)=0 then                               line\$=buff\$                         else                               line\$+=" "+buff\$                         end if                   Always                   If line\$<>"" Then Print line\$             }       }       Flush       MyFile.OpenFile       Render.Ruler &MyFile, %ToScreen=True       Render.Screen &MyFile       Print MyFile.EOF } ThirdProgram

Forth Try

So can we refine our program to print line by line, calling ScreenLine() method?

Now Render group get the buff\$ and line\$ as members, because ScreenLine method need to use them for each call.

 Module ForthProgram {       Group MyFile {       Type: File       Private:             index=100000             a\$={44             He [Sarek] was      delighted to discover   how very much like             him they    [computer people] were   ...  All they             cared about was the art of their             work, and doing            it right.  It was hard not             to admire such dedication and love of             computing for      its own   sake.             The programmers were the   first             Earth    people  he came   to   understand as being             really human.             Diane Duane.- "Spock's World."             }       Public:             Property EOF{value}=1=1             Module OpenFile {                   .index<=1                   .[EOF]<=False             }             Function GetNumber {                   m=0                   =val(mid\$(.a\$, .index),"??",m)                   .index+=m             }             Function GetWord\$ {                   while chrcode(mid\$(.a\$, .index, 1))<33                         .index++                         if .index>len(.a\$) then .[EOF]<=True: Break                   end while                   m=.index                   while chrcode(mid\$(.a\$, .index, 1))>32                         .index++                         if .index>len(.a\$) then .[EOF]=True: Exit                   end while                               =mid\$(.a\$, m, .index-m)             }       }       Group Render {             T, line\$, buff\$             Module Ruler (&File as File, ToScreen=False){                   .line\$<=""                   .buff\$<=""                   .T<=File.GetNumber()                   If .T>89 then Error "Cant handle such width"                   DRule\$=""                   for i=0 to .T div 10                         DRule\$+=If\$(i=0->string\$(" ",10),string\$(str\$(i,""),10))                   next                   If Not ToScreen Then Exit                   Print Mid\$(DRule\$,2, .T)                   Print string\$("1234567890", .T div 10)+Mid\$("1234567890",1, .T mod 10)             }             Function ScreenLine (&File as File) {                   Repeat                         if .buff\$="" then .buff\$<=File.GetWord\$(): =True                         if File.EOF then =False : exit                         if len(.line\$)+1+len(.buff\$)>.T then exit                         .line\$+=If\$(len(.line\$)=0->"", " ")+.buff\$                         .buff\$<=""                   Always                   if .line\$<>"" then Print .line\$ : .line\$<=""             }       }       Flush       MyFile.OpenFile       Render.Ruler &MyFile, %ToScreen=True       While Render.ScreenLine(&MyFile)             Wait 10       End While       Print MyFile.EOF } ForthProgram

Fifth Try

Lets say now that we want to change the loop to print word by word (not line by line as the forth program).

Now we use line\$ only for calculations. So we print word as P\$ (with or wthout a leading space) and a “;” at the end so we didn’t get a new line. We use Print with no parameters when we want to place a new line.

Fifth Program

 Module FifthProgram {       Group MyFile {       Type: File       Private:             index=100000             a\$={44             He [Sarek] was      delighted to discover   how very much like             him they    [computer people] were   ...  All they             cared about was the art of their             work, and doing            it right.  It was hard not             to admire such dedication and love of             computing for      its own   sake.             The programmers were the   first             Earth    people  he came   to   understand as being             really human.             Diane Duane.- "Spock's World."             }       Public:             Property EOF{value}=1=1             Module OpenFile {                   .index<=1                   .[EOF]<=False             }             Function GetNumber {                   m=0                   =val(mid\$(.a\$, .index),"??",m)                   .index+=m             }             Function GetWord\$ {                   while chrcode(mid\$(.a\$, .index, 1))<33                         .index++                         if .index>len(.a\$) then .[EOF]<=True: Break                   end while                   m=.index                   while chrcode(mid\$(.a\$, .index, 1))>32                         .index++                         if .index>len(.a\$) then .[EOF]=True: Exit                   end while                               =mid\$(.a\$, m, .index-m)             }       }       Group Render {             T, line\$, buff\$             Module Ruler (&File as File, ToScreen=False){                   .line\$<=""                   .buff\$<=""                   .T<=File.GetNumber()                   If .T>89 then Error "Cant handle such width"                   DRule\$=""                   for i=0 to .T div 10                         DRule\$+=If\$(i=0->string\$(" ",10),string\$(str\$(i,""),10))                   next                   If Not ToScreen Then Exit                   Print Mid\$(DRule\$,2, .T)                   Print string\$("1234567890", .T div 10)+Mid\$("1234567890",1, .T mod 10)             }             Function ScreenWord (&File as File) {                   .buff\$<=File.GetWord\$(): =True                   if File.EOF then =False : Print : exit                   if len(.line\$)+1+len(.buff\$)>.T then Print : .line\$<=""                   P\$=If\$(len(.line\$)=0->"", " ")+.buff\$                   Print P\$;                   .line\$+=P\$             }       }       Flush       MyFile.OpenFile       Render.Ruler &MyFile, %ToScreen=True       While Render.ScreenWord(&MyFile)             Wait 2       End While       Print MyFile.EOF } FifthProgram

Sixth Try

So now its time to change MyFile group. We want to have a GetChar() member. Logic for End of File now moved from GetWord() method to GetChar() method.

Method GetWord\$ change to not use the index variable, but the GetChar method.

The Z variable display the number of Words.

Sixth Program

 Module SixthProgram {       Group MyFile {       Type: File       Private:             index=100000             a\$={44             He [Sarek] was      delighted to discover   how very much like             him they    [computer people] were   ...  All they             cared about was the art of their             work, and doing            it right.  It was hard not             to admire such dedication and love of             computing for      its own   sake.             The programmers were the   first             Earth    people  he came   to   understand as being             really human.             Diane Duane.- "Spock's World."             }       Public:             Property EOF{value}=1=1             Module OpenFile {                   .index<=1                   .[EOF]<=False             }             Function GetNumber {                   =int(val(.GetWord\$()))             }             Function GetChar(&C\$) {                   If Not .[EOF] Then                         =True                         C\$=mid\$(.a\$, .index, 1)                         .index++                         if .index>len(.a\$) then .[EOF]<=True                   End If             }             Function GetWord\$ {                   W\$=""                   Word\$=""                   Do                         If Not .GetChar(&W\$) Then Break                         if chrcode(W\$)>32 Then Exit                   Always                   Word\$+=W\$                   Do                         If Not .GetChar(&W\$) Then Exit                         if chrcode(W\$)<33 Then Exit                         Word\$+=W\$                   Always                   =Word\$             }       }       Group Render {             T, line\$, buff\$             Module Ruler (&File as File, ToScreen=False){                   (.T, .line\$, .buff\$)=(File.GetNumber(), "", "")                   If .T>89 then Error "Cant handle such width"                   DRule\$=""                   for i=0 to .T div 10                         DRule\$+=If\$(i=0->string\$(" ",10),string\$(str\$(i,""),10))                   next                   If Not ToScreen Then Exit                   Print Mid\$(DRule\$,2, .T)                   Print string\$("1234567890", .T div 10)+Mid\$("1234567890",1, .T mod 10)             }             Function ScreenWord (&File as File) {                   \\ by default a arithmetic funcion return 0 (false)                   .buff\$<=File.GetWord\$()                   if File.EOF then Print : exit                   if len(.line\$)+1+len(.buff\$)>.T then Print : .line\$<=""                   P\$=If\$(len(.line\$)=0->"", " ")+.buff\$                   Print P\$;                   .line\$+=P\$                   =True             }       }       Flush       MyFile.OpenFile       Render.Ruler &MyFile, %ToScreen=True       Z=0       While Render.ScreenWord(&MyFile)       Z++       End While       Print MyFile.EOF       Print "Z=";Z } SixthProgram

Final Try

We skip some tries, to get the final try. We change MyFile Group and make the File Class. The File Class has two members, the F as the file handler, and the ANSI boolean variable to indicate if the file is ANSI or not (a UTF16LE encoding). Now the a\$ not exist. We have a file, named spoke, in current directory (dir\$ return this). Also we use a ANSI variable in Module FinalProgram to get a random number, and then we prepare a file as ANSI or UTF16LE.

Class File make MyFile object. We have new methods OpenFile, CloseFile and the constructor File (exist only when we make the object, but not to the final object because we have it after a Class: directive). The File constructor get the type of file.

So what we have to change to read from a file?

We change only the GetChar method. Files have the Eof() function so we prepare property EOF according to Eof() function. Also we have to Open the file, and at the end we have to close it.

Final Program

 Module FinalProgram {       Ansi=Random(-1, 0)       Print "We make a file in dir:"       Print Dir\$       \\ Locale used for converting properly internal UTF16LE to and from ANSI       Locale 1033       If Ansi Then             Open "spoke" for output as #F             Print "ANSI file - Using Locale=";Locale       Else             Open "spoke" for wide output as #F             Print "UTF 16LE File"       End if       Print #F, {44             He [Sarek] was      delighted to discover   how very much like             him they    [computer people] were   ...  All they             cared about was the art of their             work, and doing            it right.  It was hard not             to admire such dedication and love of             computing for      its own   sake.             The programmers were the   first             Earth    people  he came   to   understand as being             really human.             Diane Duane.- "Spock's World."             }       Close #F       Print "File Length in bytes:"; Filelen("spoke")       Class File {       Private:             F=0             Ansi=False       Public:             Property EOF{value}=1=1             Module OpenFile(name\$) {                   If .F>0 then Error "Close current file"                   If .Ansi Then                         Open name\$ for input as #.F                   else                         Open name\$ for wide input as #.F                   end if             }             Module CloseFile {                   If .F>0 then Close #.F             }             Function GetNumber {                   =int(val(.GetWord\$()))             }             Function GetChar(&C\$) {                   .[EOF]<=Eof(#.F)                   If Not .[EOF] Then                         =True                         C\$=Input\$(.F, If(.Ansi->2, 1))                         .[EOF]<=Eof(#.F)                   End If             }             Function GetWord\$ {                   W\$=""                   Word\$=""                   Do                         If Not .GetChar(&W\$) Then Break                         if chrcode(W\$)>32 Then Exit                   Always                   Word\$+=W\$                   Do                         If Not .GetChar(&W\$) Then Exit                         if chrcode(W\$)<33 Then Exit                         Word\$+=W\$                   Always                   =Word\$             }       Class:             Module File(Ftype) {                   .Ansi<=Ftype             }       }       Group Render {             T, line\$, buff\$             Module Ruler (&File as File, ToScreen=False){                   (.T, .line\$, .buff\$)=(File.GetNumber(), "", "")                   If .T>89 then Error "Cant handle such width"                   DRule\$=""                   for i=0 to .T div 10                         DRule\$+=If\$(i=0->string\$(" ",10),string\$(str\$(i,""),10))                   next                   If Not ToScreen Then Exit                   Print Mid\$(DRule\$,2, .T)                   Print string\$("1234567890", .T div 10)+Mid\$("1234567890",1, .T mod 10)             }             Function ScreenWord (&File as File) {                   \\ by default a arithmetic funcion return 0 (false)                   .buff\$<=File.GetWord\$()                   if File.EOF then Print : exit                   if len(.line\$)+1+len(.buff\$)>.T then Print : .line\$<=""                   P\$=If\$(len(.line\$)=0->"", " ")+.buff\$                   Print P\$;                   .line\$+=P\$                   =True             }       }       Flush       Print "Process"       Myfile=File(ANSI)       MyFile.OpenFile "Spoke"       Render.Ruler &MyFile, %ToScreen=True       Z=0       While Render.ScreenWord(&MyFile)             Z++       End While       Print MyFile.EOF       MyFile.CloseFile       Print "Count **words**=";Z } FinalProgram