Δευτέρα 28 Νοεμβρίου 2022

Revision 19 Version 11

 Some bugs removed, one was rare and for that reason difficult to understand, but I found it. 

This is the priority1 example in info (new example). This example make a priority queue (and a second one to show the merging function). In previous revisions, this run without error but the outcome has a missing string; Now run as expected. The problem was on the operator ">" , there is a missing of a simple statement to "clear" a specific "state" (the use of pointer to group).


This example use a class obj to make objects which hold two things, the priority level  x (low value means bigger priority) and the task name. Also we have to handle two or more tasks with same priority.

A class by default is a function which return an object (not a pointer to object). The object indeed has a pointer, but the interface has specific state to indicate the way we can use. so if we execute the statement: A=obj(1, "task title1") we get a Group, named A as bound to module/function we have this code. This group A deleted at the exit/ end of execution of module/function. This group called named group. It has a name, so we can pass it by value, or by reference like ordinary variables. If B is another named group. then A=B is a copy of B state to A state. So we understand here that A and B has no "useful" pointers. Also if we have array Alfa(1 to 10) we can do this Alfa(1)=obj(1, "task title1") which make the item at index 1, on array Alfa(), to hold an object, but not a useful pointer, at this moment. This object's life extended to Alfa() life or the assigning of a new object for the specific array item. A function may return the array, so the group in the array is like a "float" group. It goes where the container (here an array, but can be a list, or a stack). This was the way to use groups before the introducing of pointers to groups. The old way was very safe because actually the hidden pointer to group object exist only once, so there was no way to make tight link between objects, which prevent the "death", or the removing/deleted the resources which hold. All objects die when the last pointer deleted, utilizing a counter on the object state (or memory). So the simple object with a unique pointer, stayed either in variable in a module, which at the end of execution of that module the variable erased, causing the counter of pointer references to became zero. The same for the "containers", when the last pointer for a container erased, the items also erased, and everywhere there is an object of type group, that object deleted. So this can be change when we use multiple pointers for the same object of type group.

You can see in this example the use of g() function, a global function which return a pointer to group. A pointer is like a numeric value, looking the identifier, but it is "object" type. There are two kinds of pointers, and the "holded", the variable (the identifier) can use any of the two. One of them is the simple weak reference. If the A group is defined as named group in the current module, then th C->A or C=Pointer(A) define a pointer C as weak reference. So this C can be valid until the end of execution of the current module (or function). Let say group A has a property X a numeric one, then the Print A.X  print this, the AnotherModule &A.X pass the property by reference,  (we have to use &ParameterName from the other side of call, to get the weak reference and make it a strong reference). References are Identifiers which show to memory to another identifier, and can't change to show something else.  When references erased nothing happen to original value, Pointers are the tickets to travel with objects, and when the pointers erased, the objects removed from system. If A is a pointer to a group, we can use by reference pass, which means we may have B as a reference to A which is a pointer. If we pass a new pointer to B then A get this pointer too, because there is one place to hold pointer, the place of A, and B has a reference only o that place (this isn't weak, but strong, the B has a Hard Link to A). Its clear, a reference "die"  before the original identifier, except we hold the weak reference but 100% the use of the weak reference, at the time after the death of original identifier, will cause troubles,

Pointer() without parameter is the Null object, which is of type Group. We can't extend a class using the Null object. But we can put this in a pointer of group to decrement the "life counter", the counter of pointers which "refer" to group. The kind of  pointer as weak reference has no counting effect. The group dies at specific moment.

So how we can make original pointers;

Let say that A is a group, a named group. So the Beta->(A) or Beta=Pointer((A)) make a copy of A and return a pointer to this copy. If A(1) has a group then the Beta->A(1) make the two of them pointers to group (if A(1) was not a pointer to group, then change). So if we have a property X (as variable or as a group inside which we can set values, or read values or both), we can use Print Beta=>X and Beta=>X=100, and we will see that A(1).X has value 100.

So lets see PriorityQueueForGroups module. In this module we use some Subs, and simple Functions. These two kind of "named code" (called using names), are written at the end of the body of module, and they use the same namespace as of the module, so from a sub we can call any sub included in the module, and any function too. This not hold for functions/lambda functions/Modules because from that we can call anything we define only there, and any global. We can say, subs and simple functions (called with @ prefix) are used when we have state to share. We can say that modules, functions and lambda functions (which have closures), are designed to operate as units, holding separate state (modules and functions can use static variables, but this is something rare) at the moment of call, and then the next call we have a "resetting" code, to start again. So the outcome from these are predictable (but no always if we look "outside" and that change the behavior of the code). On  top of these two worlds, we have the Groups. These have state on board, and methods, and also they have private members, to allow only own methods to change them.

So the obj class has private members the X and S$ variables. So when we make an object given a priority number and a task name we haven't a way to change them (but we can define a new one and replace the pointer everywhere we hold a pointer to "old data".


All the tasks held by groups which held by a stack. The stack is a collection of values. A stack is a modified Vb6 Collection, which hold a "carrier" object (which Interpreter get them from a pool of free objects, eliminating the creation/removing of thes). That object named VarItem may hold numbers of many types, strings, objects of any kind. So here we place the pointers from g() functions, pointers to obj type object. The priority queue is a stack object and we place in an order the "task". Every time a new task added, we looking for a position to insert it, using binary search. To compare the tasks we have an operator defined in obj class. Also we have  a copy function which return a pointer to a copied "float" group. Also there is a tostring$ to show results. There is a remove method which called when a float group lost all pointer to it. (in fact there is another pointer, which we use before the final "shutdown'. Variable countmany (a long type) used to write how many objects used, i a session, of M2000 Interpreter.

Thats all for now.


global countmany=0&
class obj {
private:
      x, s$
public:
      operator ">" {
            read k as *obj
            push .x>k=>x
      }
      property toString$ {
            value (sp=8) {
                  link parent x, s$ to x, s$
                  value$=format$("{0::-5}"+string$(" ", sp)+"{1:20}", x, s$)
            }
      }
      function Copy {
            countmany++
            z=this
            =pointer((z))
      }
      remove {
            countmany--
      }
class:
      module obj (.x, .s$) {countmany++}
}
// obj() return object as value (using a special pointer)
function global g(priority, task$) {
// here we return an object using nonrmal pointer
// try to change -> to = to see the error
->obj(priority, task$)
}
Module PriorityQueueForGroups (emptysecndqueue as boolean) {
      Flush ' empty current stack
      Data g(3, "Clear drains"),g(4 ,"Feed cat"), g( 5 , "Make tea")
      Data g( 1 ,"Solve RC tasks")
      ObjectCount()
      pq=stack
      zz=stack
      while not empty
            InsertPQ(pq) // top of stack is pq then objects follow
      end while
      Pen 15 {
            data g(2 , "Tax return"), g(1 ,"Solve RC tasks#2")
            while not empty: InsertPq(zz): End While
            n1=each(zz,-1,1)
            Header()
            while n1
                  Print @Peek$(stackitem(n1))
            end while
      }
      MergePq(pq, zz, emptysecndqueue)
      InsertPq(pq, g(1 ,"Solve RC tasks#3"))
      ObjectCount()
      Print "Using Peek to Examine Priority Queue"
      n1=each(pq,-1, 1)
      Header()
      while n1
            Print @Peek$(stackitem(n1))
      end while
      ObjectCount()
      Header()
      while not @isEmpty(pq)
            Print @Pop(pq)=>tostring$
      end while
      ObjectCount()
      Header()
      while not @isEmpty(zz)
            Print @Pop(zz)=>tostring$
      end while
      ObjectCount()
      // here are the subs/simple functions
      // these are static parts of module
      sub Header()
            Print " Priority        Task"
            Print "==========  ================"
      end sub
      sub ObjectCount()
            Pen 10 {Print "There are ";countmany;" objects of type obj"}
      end sub
      sub MergePq(a, pq, emptyqueue)
            local n1=each(pq, -1, 1), z=pointer()
            while n1
                if emptyqueue then
                    stack pq {
                        shiftback len(pq)
                        InsertPQ(a, Group)
                    }
                else
                    z=stackitem(n1)
                    InsertPQ(a, z=>copy())
                end if
            end while
      end sub
      sub InsertPQ(a, n as *obj)
            Print "Insert:";n=>tostring$(1)
            if len(a)=0 then stack a {data n} : exit sub
            if @comp(n, stackitem(a)) then stack a {push n} : exit sub
            stack a {
                  push n
                  local t=2, pq=len(a), t1=0
                  local m=pq
                  while t<=pq
                        t1=m
                        m=(pq+t) div 2
                        if m=0 then m=t1 : exit
                        If @Comp(stackitem(m),n) then t=m+1: continue
                        pq=m-1
                        m=pq
                  end while
                  if m>1 then shiftback m
            }
      end sub
      function comp(a as *obj, pq as *obj)
            =a>pq
      end function
      function Peek$(a as *obj)
            =a=>toString$
      end function
      function IsEmpty(a)
            =len(a)=0
      end function
      function Pop(a)
            // Group make a copy (but here is a pointer of group)
            stack a {shift stack.size
            =Group}
      end function
}
pen 11 {Print "Priority Queue - Merging and removing items from second Queue"}
PriorityQueueForGroups true
Print "press a key": Push Key$: Drop
Pen 11 {Print "Priority Queue - Merging without removing items from second Queue"}
PriorityQueueForGroups false


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

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

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