Τρίτη, 22 Δεκεμβρίου 2015

List Processing in M2000 (New)

Επειδή στο Insomnia.gr βλέπουν τα Ελληνικά και τους κακοφαίνονται, έγραψα αυτό εδώ το "δοκιμαστικό" πρόγραμμα

Έχει ένα στατικό αντικείμενο για να διαχειρίζεται τυχόν λάθη του Iterator. Έχουμε τρεις κλάσεις: Iterator, MyList και Container
Έχουμε ένα τμήμα εργασίας PrintList και ένα ακόμη CheckMe
Έχουμε μια ψεύτικη συνάρτηση την forlazy() γιατί αν κληθεί ως έχει θα δώσει λάθος. Η χρήση της είναι για να φτιάξουμε μια ανώνυμη συνάρτηση με την Lazy$() και να την τοποθετήσουμε  στο τμήμα CheckMe. Στην ουσία μεταφέρουμε ένα τμήμα του αρχικού μέσα στην ανώνυμη συνάρτηση. Μπορούμε να έχουμε side effects αν θέλουμε. Ως συνάρτηση θα έχει δικό της σωρό τιμών (την ειδική στοίβα τιμών της Μ2000), αλλά θα έχει το όνομα του τμήματος. Δηλαδή μπορεί να βλέπει ότι βλέπουμε στο αρχικό τμήμα. Έτσι μπορούμε να καλέσουμε την  PrintList (ενώ μόνο αν ήταν Global θα μπορούσαμε να το κάνουμε) αλλά και οι παράμετροι είναι το b από το αρχικό τμήμα. Μάλιστα εδώ βάζουμε τιμή από το CheckMe. Ουσιαστικά είναι μια κλήση προς τα πίσω! Καλούμε την CheckMe και αυτή καλεί χωρίς να το γνωρίζει με κάποιο τρόπο, πέντε φορές αυτόν που την κάλεσε και μετά γυρίζει πίσω!
Δεν χρησιμοποιώ εδώ πέρασμα αντικειμένου με αναφορά. Όλα είναι με αντιγραφή.
Δοκιμάστε να δώσετε μηδενικό βήμα στον Iterator να δείτε πως δουλεύει το Exception.


Global Group Raise {
      A$, N
      Module Exception {
            If Match("SN") Then Read .A$, .N
            Error .A$
      }
      Module FlushIt {
      .A$<=""
      .N<=0
      Flush Error
      }
}
Class iterator {
      v, vmax, vstep=0
      vname$
      Dim backup(4)
      Module iterator {
            Read .vname$, .v, .vmax
            If Match("N") Then { Read .vstep } Else .vstep<=1
             .backup(0) := .vname$, .v, .vmax, .vstep
      }
      Module reset {
            Stock .backup(0) Out .vname$, .v, .vmax, .vstep
      }
      Module next_step {
            .v+=.vstep
            Select Case sgn(.vstep)
            Case 1
                  If .v>.vmax Then Raise.Exception "Out of limit: "+.vname$, 100
            Case -1
                  If .v<.vmax Then Raise.Exception "Out of limit: "+.vname$, 101
            Else
                  Raise.Exception "Zero step: "+.vname$, 102
            End Select
      }
      Function next2 {
            Try { .next_step }
            If Raise.N<>0 Then {
                  If Raise.N=102 Then { Raise.Exception } Else { =False }
            } Else =True
            Raise.FlushIt
      }
}
\\ phase one
Form 60,32
a=iterator("alfa",3,1,-1)
List ! \\ just put list for phase one


\\phase two just iterate
Do {
      Print a.v
} Until Not a.next2()
a.Reset
Do {
      Print a.v
} Until Not a.next2()
Print
\\phase 3 lets make a list
Class MyList {
      Paramlist$
      Module Mylist {
            Read .Paramlist$
      }       
      Module Add {
            Read What$
            .Paramlist$<=.Paramlist$+What$
      }
      Function ListLen {
            buf$=.Paramlist$
            Stack buf$ \\ Stack remove data from buf$
            =Stack.size
            \\ Stack now dropped automatic
      }
      \\ we use Paramlist$() for same interface..see Container
      Function Paramlist$ {
            =.Paramlist$
      }
}
\\ we make L as Mylist Class
L=Mylist(Stack$(1,2,"ok",4,5,6,7))
L.Add Stack$(8,9,10)
\\ This is our working module
\\ We use two groups for input by value (a copy of each)
\\ One Mylist and one Iterator
\\ Modules get parent Stack so we open a New Stack
\\ So stack.size return True size
Module PrintList {
      Read listgroup, iter
      Stack New {
            Stack listgroup.Paramlist$()
            If stack.size>0 Then {
                  Do {
                    Select Case stacktype$(iter.v)
                          Case "Number"
                                Print stackitem(iter.v),
                          Case "String"
                                Print stackitem$(iter.v),
                          Case Else
                                Print "-------",
                    End Select
                  } Until Not iter.next2()
                  Print
            }
      }
}
\\ Now we Try
PrintList L, iterator("MyList L",1,L.Listlen(),2)
\\ phase 4
\\ We make another Class
\\ Using an array internal
Class Container {
      Dim Buf()
      N
      Module Container {
            Dim .Buf()
            .N<=0
            If Match("G") Then .Add \\ supose Stack has a Mylist copy
      }
      Module Add {
            Read kList
            K=klist.listlen()
            If K>0 Then {
                  Dim .Buf(K+.N)
                  Param$=klist.Paramlist$()
                  If Asc(Param$)=2 Then {
                        .Buf(.N):=Param$(Param$)
                  } Else {
                        .Buf(.N):=Param(Param$)
                  }
                  .N+=K
            }
      }
      Function GetValue$ {
            Read Index%
            Index%--
            If Index%>=0 and Index%<.N Then {
                  Link .Buf() To B$()
                  =B$(Index%)
            } Else =""
      }
      Function Paramlist$ {
            If .N=0 Then { =""
                  } Else {
                  Stock .Buf(0) Keep .N, all$
                  =all$
            }
      }
      Function ListLen {
            =.N
      }
}
b=Container() \\  b=Container(L)  \\ check this also
b.Add Mylist(Stack$(100,2,3,4,5))
b.Add Mylist(Stack$(50,"Yes",40,20,10))
\\ the same Call but now we have Container with same interface
\\ for ListLen() and Paramlist$()
\\ We pass it by Value - a copy of b
PrintList b, iterator("Container b",1,b.ListLen())
For b {
      Report format$("Item 7 ={0}  and Item 10 ={1}   - List 1st item at index 1",.GetValue$(7), .GetValue$(10))
}
\\Phase 5
\\ We drop list In B and we copy a new list from L
\\ b.Container b is Not a fault because first a copy created.
b.Container L
function forlazy {
      \\ this is Not a normal function
      \\ is a part of the module
      \\ and that part executed as lazy evalution
      \\ In module checkme
      \\ Each time we Call this part a new A defined
      \\ but when the Call ends any new variable deleted
      \\ We use New to make a new variable for sure
      Read New A
      PrintList b, iterator("Container b",A,b.ListLen())
}
\\Phase 6
\\ We can put PrintList (a module) into another module
\\ We make a fake function forlazy() for this
\\ It is fake because Lazy$() put a command as first line
\\ with the name of this module. So we copy the name space
\\ Calling w() is like calling a part this module
\\ But see CheckMe send back the K parameter
Module CheckMe {
      Read &w()
      For K=1 To 5
            Print ">>>",
            Call w(K)
      Next K
}
CheckMe Lazy$(&forlazy())


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

\\ η ομάδα αυτή είναι στατικό αντικείμενο
\\ το οποίο θα υπάρχει όσο αυτό το τμήμα τρέχει
\\ και θα το βλέπουν και όλα τα άλλα τμήματα που
\\ θα δημιουργηθούν σε αυτό.
Γενική Ομάδα Raise {
      A$, N
      Τμήμα Exception {
            Αν Ταύτιση("ΓΑ") Τότε Διάβασε .A$, .N
            Λάθος .A$
      }
      Τμήμα FlushIt {
      .A$<=""
      .N<=0
      Άδειασε Λάθος
      }
}
\\ στις κλάσεις μπορούμε να μην δίνουμε τιμή στις μεταβλητές της
\\ η κλάση είναι συνάρτηση που γυρίζει ένα αντικείμενο με το περιεχόμενό της
\\ Αν υπάρχει τμήμα με ίδιο όνομα τότε καλείται ως κατασκευαστής.
\\ εδώ υποχρεωτικά θα πρέπει να δώσουμε τρεις τιμές
\\ ένα όνομα-περιγραφή, την αρχική τιμή και την τελική
\\ αν θέλουμε δίνουμε και το βήμα αλλιώς θα είναι 1
\\ Οι μεταβλητές με τελεία είναι οι μεταβλητές του αντικειμένου
\\ Αν θέλουμε να αλλάξουμε άμεσα τιμή με σταθερά όπως το 1
\\ τότε χρησιμοποιύμε το <=  (αλλλιώς θα δημιουργήσουμε τοπική)
Κλάση iterator {
      v, vmax, vstep=0
      vname$
      Πίνακας backup(4)
      Τμήμα iterator {
            Διάβασε .vname$, .v, .vmax
            Αν Ταύτιση("Α") Τότε { Διάβασε .vstep } Αλλιώς .vstep<=1
            .backup(0) := .vname$, .v, .vmax, .vstep
      }
      Τμήμα reset {
            Στοκ .backup(0) Σε .vname$, .v, .vmax, .vstep
      }
      Τμήμα next_step {
            .v+=.vstep
            Επίλεξε Με sgn(.vstep)
            Με 1
                  Αν .v>.vmax Τότε Raise.Exception "Out of limit: "+.vname$, 100
            Με -1
                  Αν .v<.vmax Τότε Raise.Exception "Out of limit: "+.vname$, 101
            Αλλιώς
                  Raise.Exception "Zero step: "+.vname$, 102
            Τέλος Επιλογής
      }
      Συνάρτηση next2 {
            Δες { .next_step }
            Αν Raise.N<>0 Τότε {
                  Αν Raise.N=102 Τότε { Raise.Exception } Αλλιώς { =Ψευδής }
            } Αλλιώς =Αληθής
            Raise.FlushIt
      }
}
\\ Φάση 1
Φόρμα 60,32
a=iterator("alfa",3,1,-1)
Λίστα ! \\ just put Λίστα Για phase one
\\ Φάση 2 χρήση του Iterator
Επανέλαβε {
      Τύπωσε a.v
} Μέχρι Όχι a.next2()
a.Reset
Επανέλαβε {
      Τύπωσε a.v
} Μέχρι Όχι a.next2()
Τύπωσε
\\ Φάση 3 χρήση της MyList κλάσης
Κλάση MyList {
      Paramlist$
      Τμήμα Mylist {
            Διάβασε .Paramlist$
      }       
      Τμήμα Add {
            Διάβασε What$
            .Paramlist$<=.Paramlist$+What$
      }
      Συνάρτηση ListLen {
            buf$=.Paramlist$
            Σωρός buf$ \\ Σωρός remove data from buf$
            =μέγεθος.σωρού
            \\ Σωρός now dropped automatic
      }
      \\ we use Paramlist$() Για same interface..see Container
      Συνάρτηση Paramlist$ {
            =.Paramlist$
      }
}
\\ Φτιάχνουμε την στατική L  ως Mylist Κλάση
L=Mylist(Σωρός$(1,2,"ok",4,5,6,7))
L.Add Σωρός$(8,9,10)
\\ Τώρα θέλουμε ένα Τμήμα που θα κάνει εμφάνιση τα στοιχεία
\\ Θα δείνουμε δυο αντικείμενα, ένα με τα στοιχεία και ένα με το πώς θα διαβάζουμε
\\ Η εντολή Σωρός με αλφαριθμητικό κοιτάει αν υπάρχουν στοιχεία και κάνει συγχώνευση στο
\\ άνω μέρος. ώστε το πρώτο στο αλφαριθμητικό θα είναι κορυφή στο σωρό
\\ Αντί όμως να διαβάζουμε τα στοιχεία για να τα βγάλουμε με την Διάβασε
\\ Διαβάζουμε το σωρό σαν πίνακα!
Τμήμα PrintList {
      Διάβασε listgroup, iter
      Σωρός Νέος {
            Σωρός listgroup.Paramlist$()
            Αν μέγεθος.σωρού>0 Τότε {
                  Επανέλαβε {
                    Επίλεξε Με σωρουτυπος$(iter.v)
                          Με "Number"
                                Τύπωσε τιμησωρού(iter.v),
                          Με "String"
                                Τύπωσε τιμησωρού$(iter.v),
                          Με Αλλιώς
                                Τύπωσε "-------",
                    Τέλος Επιλογής
                  } Μέχρι Όχι iter.next2()
                  Τύπωσε
            }
      }
}
\\ Τώρα περνάμε το L και φτιάχνουμε ένα iterator άμεσα!
\\ ο οποίος δεν θα υπάρχει στο πέρας της PrintList
\\ Περνάμε εδώ με τιμή, δηλαδή με αντιγραφή την L
PrintList L, iterator("MyList L",1,L.Listlen(),2)
\\  Φάση 4 συνεχίζουμε φτιάχνοντας μια κλάση παρόμοια
\\  Αλλά εδώ χρησιμοποιούμε πίνακα που τον αυξάνουμε προοδευτικά.
Κλάση Container {
      Πίνακας Buf()
      N
      Τμήμα Container {
            Πίνακας .Buf()
            .N<=0
            Αν Ταύτιση("Ο") Τότε .Add \\ Κοιτάμε αν υπάρχει ομάδα
      }
      Τμήμα Add {
            Διάβασε kList \\ αν υπάρχει τότε τη σηκώνουμε από το σωρό.
            K=klist.listlen()
            Αν K>0 Τότε {
                  Πίνακας .Buf(K+.N)
                  Param$=klist.Paramlist$()
                  Αν Κωδ(Param$)=2 Τότε { \\ το 2 είναι όριο που ξεκινάει αλφαριθμητικό στο σωρό τύπου αλφαριθμητικό.
                        .Buf(.N):=Πάραμ$(Param$) \\ μετατρέπουμε το σωρό σε σειρά στοιχείων, πρώτο στοιχείο αλφαριθμητικό
                  } Αλλιώς {
                        .Buf(.N):=Πάραμ(Param$) \\ το ίδιο και εδώ αλλά το πρώτο στοιχείο αριθμός
                  }
                  .N+=K
            }
      }
      Συνάρτηση GetValue$ {
            Διάβασε Index% \\ χρησιμοποιούμε ακέραιο με βάση το 1
            Index%-- \\ εσωτερικά με 0
            Αν Index%>=0 Και Index%<.N Τότε {
                  Ένωσε .Buf() Στο B$() \\ το B$() δείχνει τα στοιχεία του .Buf() ως αλφαριθμητικά
                  \\ οι πίνακες στην Μ2000 έχουν στοιχεία τύπου Variant. Μπορεί να είναι αριθμοί, αλφαριθμητικά, αντικείμενα.
                  =B$(Index%) \\ οπότε γυρνάμε αλφαριθμητικά
            } Αλλιώς =""
      }
      Συνάρτηση Paramlist$ {
            Αν .N=0 Τότε { =""
                  } Αλλιώς {
                  Στοκ .Buf(0) για .N, all$ \\ εδώ εξάγουμε όλα τα στοιχεία σε σωρό τύπου αλφαριθμητικού
                  =all$
            }
      }
      Συνάρτηση ListLen {
            =.N
      }
}
b=Container() \\  b=Container(L)  \\ δοκίμασε με παράμετρο το αντικείμενο L
b.Add Mylist(Σωρός$(100,2,3,4,5))
b.Add Mylist(Σωρός$(50,"Yes",40,20,10))
\\ Τώρα θα δοκιμάσουμε με το αντικείμενο b
\\ Το οποίο έχει κοινά χαραξτηριστικά με το L (αυτό το λένε σε άλλες γλώσσες....interface)
\\ Έτσι μπορεί να το χρησιμοποιήσει η PrintList
PrintList b, iterator("Container b",1,b.ListLen())
Για b {
      Αναφορά μορφή$("Item 7 ={0}  Και Item 10 ={1}   - Λίστα 1st item at index 1",.GetValue$(7), .GetValue$(10))
}
\\ Φάση 5
\\ Πετάμε το πίνακα του b βάζοντας ένα νέο από το L
\\ b.Container b is Όχι a fault because first a copy created.
b.Container L
Συνάρτηση forlazy {
      \\  Αυτή είναι μια ψεύτικη συνάρτηση
      \\ Έχει όνομα για να την βρίσκει η οκν$()
      \\ Ο κώδικάς της θα αντιγραφτεί ως ανώνυμη συνάρτηση
      \\ Όταν τρέξει όμως θα έχει πάρει το όνομα χώρου
      \\ του τμήματος που βρίσκεται!
      \\ Έτσι θα μπορούμε να καλέσουμε την PrintList σε "καλεσμένο" τμήμα
      \\  Αυτό σημαίνει ότι αν αλλάξω μια Α τότε μπορεί να αλλάξει τιμή στο τμήμα
      \\ Αλλά πρόσκαιρα μπορώ να δημιουργήσω μια νέα Α
      \\ ο διακόπτης Νέο κάνει τη διάβασε να βάζει αναφορά σε όνομα που ήδη έχει αναφορά.
      \\ Αλλά όσο υπάρχει το νέο όνομα το παλιό ίδιο όνομα δεν θα είναι στο προσκήνιο
      Διάβασε Νέο A
      PrintList b, iterator("Container b",A,b.ListLen())
      \\ στο τέλος της συνάρτησης αν θέλουμε δίνουμε επιστροφή
      \\ εδώ την καλούμε με Κάλεσε που σημαίνει ότι μη μηδενική τιμή θα είναι λάθος!
}
\\ Φάση 6 και τελική
\\ Εδώ θέλουμε να δείξουμε ότι μπορούμε να καλέσουμε ένα τμήμα
\\ και να συμβεί το εξής: Το καλεσμένο τμήμα θα τρέξει πέντε φορές
\\ ένα τμήμα του τμήματος που το κάλεσε!
\\ Μάλιστα θα του περάσει και έναν αριθμό, το K
\\ Αυτό γίνεται γιατί η Όκν$() παράγει μια ανώνυμη συνάρτηση
\\ Την διαβάζουμε ως αναφορά σε συνάρτηση στο &w()
\\ Άλλα όταν την καλέσουμε μια εντολή στην αρχή που έχει τοποθετήσει η Όκν$()
\\ θα ενώσει το όνομα χώρου της συνάρτησης με το όνομα χώρου του τμήματος
\\ αυτού που έχουμε τις σημειώσεις!
\\ Εδώ χρησιμοποιώ και την Για Επόμενο.


Τμήμα CheckMe {
      Διάβασε &w()
      Για K=1 Εώς 5
            Τύπωσε ">>>",
            Κάλεσε w(K)
      Επόμενο K
}
CheckMe Οκν$(&forlazy())