Τρίτη 14 Ιουλίου 2020

Iterator Pattern (OOP)

Two concrete iterators, one for a string and another one for an array.  Because currentitem maybe is a string or a number we have two versions one as currentItem$ for strings. (M2000 use $ as last character  for string type identifiers, so A$, A$() are names which return string values)

I add a final While using the comma syntax (in M2000) to introduce a double walk through iteration while both iterators have something to show. The comma syntax is like AND operator in While.
Press spacebar to advance to next test.

Class Iterator {
      module first() {
            error "abstract"
      }
      function next() {
            error "abstract"
      }
      function currentItem() {
            error "abstract"
      }
      function currentItem$() {
            error "abstract"
      }
}
Class ConcreteIterator as Iterator {
Private:
      stringval$, p=0
Public:
      module first() {
            .p<=0
      }
      function next() {
            if .p<len(.stringval$) then .p++ : =true
      }
      function currentItem$() {
            =mid$(.stringval$, if(.p=0->1, .p), 1)
      }
Class:
      module ConcreteIterator (.stringval$) {
      }
}
Class ConcreteIterator1 as Iterator {
Private:
      v=(,), p=0, top
Public:
      module first() {
            .p<=-1
      }
      function next() {
            if .p<.top then .p++ : =true
      }
      function currentItem() {
            =.v#val(if(.p=-1->0, .p))
      }
Class:
      module ConcreteIterator1 {
            \\ get current stack  (leaving an empty stack) and convert it to array
            .v<=Array([])
            .top<=len(.v)-1
      }
}
Z=ConcreteIterator("ABCDEF")
Z.first
While Z.next()
      Print Z.currentItem$()
End While
print key$
M=ConcreteIterator1(10,3,12,5,2,1,300,-5,3)
M.first
While M.next()
      Print M.currentItem()
End While
print key$
M.first
Z.first
While M.next(), Z.next()
      Print M.currentItem(), Z.currentItem$()
End While
print key$
M.first
Z.first
While  Z.next()
      call void m.next()
      Print M.currentItem(), Z.currentItem$()
End While
While  M.next()
      Print M.currentItem(), Z.currentItem$()
End While


From version 12 we can use string variables without suffix $. (Although we can use it. So the previous  example works fine). String function like Mid$() has no Mid() equivalent. I keep the suffix $ for one reason:. Functions with $ are self explain about return type. Because M2000 distinguish  a$ and a as two different names (which are, the second one has one letter more), some internal read only variables like Key$ have the suffix $ (from the first version of M2000), and so go on as is.

Also I show how we can use an array using Dim which produce copies when we copy the group to another group. Also we have a derived class which constructor call the constructor of the base class. All members after class: label not included to final object (but exist at the construction).

Class Iterator {
      module first() {
            error "abstract"
      }
      function next() {
            error "abstract"
      }
      function currentItem() {
            error "abstract"
      }
}
Class ConcreteIterator as Iterator {
Private:
      string stringval
      long p=0
Public:
      module first() {
            .p<=0
      }
      function next() {
            if .p<len(.stringval) then .p++ : =true
      }
      function currentItem() {
            =mid$(.stringval, if(.p=0->1, .p), 1)
      }
Class:
      module ConcreteIterator (.stringval) {
      }
}
Class ConcreteIterator1 as Iterator {
Private:
      object v=(,)
      long p=0, top
Public:
      module first() {
            .p<=-1
      }
      function next() {
            if .p<.top then .p++ : =true
      }
      function currentItem() {
            =.v#val(if(.p=-1->0, .p))
      }
Class:
      module ConcreteIterator1 {
            \\ get current stack  (leaving an empty stack) and convert it to array
            .v<=Array([])
            .top<=len(.v)-1
      }
}
Z=ConcreteIterator("ABCDEF")
Z.first
While Z.next()
      Print Z.currentItem()
End While
print key$
M=ConcreteIterator1(10,3,12,5,2,1,300,-5,3)
M.first
While M.next()
      Print M.currentItem()
End While
print key$
M.first
Z.first
While M.next(), Z.next()
      Print M.currentItem(), Z.currentItem()
End While
print key$
M.first
Z.first
While  Z.next()
      call void m.next()
      Print M.currentItem(), Z.currentItem()
End While
While  M.next()
      Print M.currentItem(), Z.currentItem()
End While


Class ConcreteIterator2 as Iterator {
Private:
      dim v()
      long p=0, top
Public:
      module first() {
            .p<=-1
      }
      function next() {
            if .p<.top then .p++ : =true
      }
      function currentItem() {
            =.v(if(.p=-1->0, .p))
      }
Class:
      module ConcreteIterator1 {
            \\ get current stack  (leaving an empty stack) and convert it to array
            .v()=Array([]) // no used <= here, arrays with ()  defined with DIM
            .top<=len(.v())-1
      }
}
Class DerivedClass1 as ConcreteIterator2 {
Module ChangeValue (p as long, q) {
.v(p)=q
}
Class:
module DerivedClass1 {
.ConcreteIterator1
}
}
M=DerivedClass1(10,3,12,5,2,1,300,-5,3)
M.ChangeValue 4, "Hello"
M.first
While M.next()
      Print M.currentItem(),
End While
Print
// now copy of private .v() done like arrays with parenthesis (copy of array, not copy of pointer)
K=M
M.ChangeValue 4, 500
M.first
While M.next()
      Print M.currentItem(),
End While
Print
K.first
While K.next()
      Print K.currentItem(),
End While
Print

This section shows the pointers to groups

  • M and K aren't pointers to groups, but they have an object  (with a hidden pointer) to a list of members (which is a Group object). All members (methods/properties) are separete entitites local to this module.
  • When we get a copy and before do anything with it a float group produced, where all members are inside the object (also there is a list of types according to the way we make it). A group may have other groups also (like a tree of groups), or pointers to groups (like a structure with pointers).
  • If we pass the float object to an array item stay as float group.
  • If we pass the float group in new name we get an object which is a list of members, which members are local to module (and have  prefix the name of object).
  • If we place the float object to an already defined object, so the result depends ot the use of Set member if exist, otherwise we get a merge, so the old object get more members if that members didn't exist and get the type of the merged object too (added to type list). So this is a way to make objects from other objects without using classes but instances. There is a With operator which merge to float groups to a float group.
  • So the pointer to group is one of two kinds, a real pointer or a reference to a named group (a not float group). We don't know at run time which kind is a pointer to group. But a fake pointer (a reference to group) must be used before the object destroyed. The real pointer hold the object alive until the last pointer get the Null object or get out of scope.
  • About stack. We can push an object to stack. A named group pass a copy as float group. An array item as a float group pass a copy as float group. A pointer to group pass the pointer. Actually a pointer to group is a group, which points to another group or has a reference to another group (if it isn't float).
  • Some groups return value. We can get the float group using Group(nameOfgroup). When we get the pointer we can use Eval(pointer2Group) to get the value of group (if group has Value member)
  • Operators for groups work for named, float and pointed groups the same.

// p->(k)  // same as p=pointer((k))
p=pointer((k)) // get a copy of k and make a pointer to that copy
// q->k  same as q=pointer(k)
q=pointer(k) // it insn't a real pointer, but a reference to k, which work like a pointer
Print not p is k // true
Print p is type Iterator // true
Print q is k // true
Print q is type Iterator // true
k.ChangeValue 4, 1000
// we can use for object {} structure
for p {
    .first
    While .next()
          Print .currentItem(),
    End While
    Print
}
// or we can use immediate => use of object's members
q=>first
While q=>next()
      Print q=>currentItem(),
End While
Print
p=q // pointers can get another "value", any kind of pointer (real or refernece)
for p {
    .first
    While .next()
          Print .currentItem(),
    End While
    Print
}
p=pointer() // now get the null pointer
Print p is type Null // true




Arrays of Groups
  • Using << we place different object
  • An array of groups has the float groups with empty code on functions/modules members, and these exist on array object. When we extract a group we get the code of that group in the group (as float group). Only arrays which initialised with a group or a class can do that. If we put a different object to this array, that object hold the functions/modules members as is. But if we pass a same type group as the "group type of a array" the code stripped from float object.


Dim Iter1(10)<<DerivedClass1(10,random(3, 10),12,5)
Iter1(3).first
While Iter1(3).next()
      Print Iter1(3).currentItem(),
End While
Print
p=pointer(Iter1(3))
// we can get a pointer, but now Iter1(3) hasn't a float group, but a pointer which hold the float group.
for p {
    .first
    While .next()
          Print .currentItem(),
    End While
    Print
}
p=>changevalue 1, 300
// now Iter(3) at index 1 (second item) has value 300
for Iter1(3) {
    .first
    While .next()
          Print .currentItem(),
    End While
    Print
}
Print p is iter1(3) // true




Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.