M2000
has two kind of objects when we think about pointers. An Inventory is
an object, a list of keys, or keys and data (pairs). This kind of
object is a reference type always. We can make as many variables, or
array items, or inventory items, or stack items to point to one
Inventory.
Another
type of object is the Group Object. This object has only one pointer
to hold it, and is internal, and not exposed to user. So we can hold
in one place only.
There
is one type of reference, a reference by name, or Weak reference. We
can pass a Group in a module using a reference to "current"
name of group. We use & before name and actually we pass a string
with full name of Group (we see in source always a name which is a
small part of current name, Interpreter expand names, and use
expansion for scoping purposes).
(
Newer versions support pointers to groups, to variables, and with the capability to have an internal number of how many pointers point to object, so when the last pointer dropped, the object deleted
check this 26/2/2019
)
(
Newer versions support pointers to groups, to variables, and with the capability to have an internal number of how many pointers point to object, so when the last pointer dropped, the object deleted
check this 26/2/2019
)
A group
may consist of many things, as data and as modules/functions. A
module in a group has access to group data using . or This. (it is
the same, but sometimes . is not reference to This. so we have
explicit declare it, as we can see in the following examples )
This is
a Group and some functionality:
Group
Alfa {
Private:
X=10, Y=3
Public:
Module SetXY ( .X, .Y) {}
Module SetY ( .Y) {}
Module SetX ( .X) {}
Module GetInfo {
Print .X, .Y
}
}
Private:
X=10, Y=3
Public:
Module SetXY ( .X, .Y) {}
Module SetY ( .Y) {}
Module SetX ( .X) {}
Module GetInfo {
Print .X, .Y
}
}
Alfa.SetXY 20,30
Alfa.GetInfo
Alfa.SetX 40
Alfa.GetInfo
Alfa.SetY 20
Alfa.GetInfo
Group
Rules
A group
is a prototype object. If we execute Beta=Alfa we get a Beta as a
Copy of Alfa (not a reference). If Beta isn't new variable the it has
to be an object (with any members, or no members), else we get an
error. So when Beta is an object, then statement Beta=Alfa make a
merging to Beta, so Beta is an Alfa and anything other members with
names that not exist in Alfa. Groups in Arrays, Inventories, Stacks,
can be merged, we put always a new one so B(2)=Alfa make a copy to
B(2), and any object in B(2) before the copy just dropped
We can
make Classes as functions which produce Groups too:
Class
Alfa {
Private:
X=10, Y=3
Public:
Module SetXY ( .X, .Y) {}
Module SetY ( .Y) {}
Module SetX ( .X) {}
Module GetInfo {
Print .X, .Y
}
Class:
Module Alfa {
If Match("NN") Then Read .X, .Y : Exit
If Match("N") and Stack.Size=1 then Read .X: Exit
If Match("?N") Then Read ? .X, .Y
}
}
Private:
X=10, Y=3
Public:
Module SetXY ( .X, .Y) {}
Module SetY ( .Y) {}
Module SetX ( .X) {}
Module GetInfo {
Print .X, .Y
}
Class:
Module Alfa {
If Match("NN") Then Read .X, .Y : Exit
If Match("N") and Stack.Size=1 then Read .X: Exit
If Match("?N") Then Read ? .X, .Y
}
}
Alfa1=Alfa()
Alfa2=Alfa(100)
Alfa3=Alfa(100, 40)
Alfa4=Alfa(, 40)
Alfa1.GetInfo
Alfa2.GetInfo
Alfa3.GetInfo
Alfa4.GetInfo
Newer version can use this (automatic handle of optional variables).:
Class Alfa {
Private:
X=10, Y=3
Public:
Module SetXY ( .X, .Y) {}
Module SetY ( .Y) {}
Module SetX ( .X) {}
Module GetInfo {
Print .X, .Y
}
Class:
Module Alfa (.X,.Y){
}
}
Alfa1=Alfa()
Alfa2=Alfa(100)
Alfa3=Alfa(100, 40)
Alfa4=Alfa(, 40)
Alfa1.GetInfo
Alfa2.GetInfo
Alfa3.GetInfo
Alfa4.GetInfo
As we see in example above, we can use optional parameters. All arguments pushed in as stack for values, and modules and functions read this stack. But read the rules:
Stack Rules
A module get parent stack, and is responsible to leave stack cleaned from passing values. A function get a new stack and can leave anything, because stack destroyed after function call.
In Class function we have a constructor as a Module but this is called when we use class name as function, so module receive stack from function, so we can leave parameters and at return from function these erased. Because we can use constructor (if we place it before Class: label), as a module call, then we have to do something with garbages..on stack. So Constructor normally exist after a Class: label, which Interpreter use as they are internal in Group, but not exported by class function. (Class function make a closed group, and then no class: type data and modules/functions can be copied).
This is an example to illustrate this idiom for M2000 (happen when we use optional parameters):
Class
Alfa {
Private:
X=10, Y=3
Public:
Module SetXY ( .X, .Y) {}
Module SetY ( .Y) {}
Module SetX ( .X) {}
Module SetAny {
If Match("NN") Then Read .X, .Y : Exit
If Match("N") and Stack.Size=1 then Read .X: Exit
If Match("N?") or Match("?N") Then Read ? .X, .Y
}
Module GetInfo {
Print .X, .Y
}
Class:
Module Alfa {
.SetAny
}
}
Private:
X=10, Y=3
Public:
Module SetXY ( .X, .Y) {}
Module SetY ( .Y) {}
Module SetX ( .X) {}
Module SetAny {
If Match("NN") Then Read .X, .Y : Exit
If Match("N") and Stack.Size=1 then Read .X: Exit
If Match("N?") or Match("?N") Then Read ? .X, .Y
}
Module GetInfo {
Print .X, .Y
}
Class:
Module Alfa {
.SetAny
}
}
Alfa1=Alfa()
Alfa2=Alfa(100)
Alfa3=Alfa(100, 40)
Alfa4=Alfa(, 40)
Alfa1.GetInfo
Alfa2.GetInfo
Alfa3.GetInfo
Alfa4.GetInfo
Alfa4.SetAny 1,2
Alfa4.GetInfo
\\ not secure because maybe stack has some values
Alfa4.SetAny 100
Alfa4.GetInfo
Alfa4.SetAny ,40
Alfa4.GetInfo
\\ This is secure for modules, because we pass two items
\\ and module take two.
Alfa4.SetAny 50,?
Alfa4.GetInfo
Student-Course Example
So we learn something about Groups and here is a way
to Link a group to another group using a weak reference (as a string)
\\
Student
Class Student {
Private:
StudentName$
CourseWeak$
Public:
Module Info {
Print "StudentName "; .StudentName$
If .CourseWeak$<>"" Then {
Print "Course:"
Rem "One Way": Print "Course:"; Eval$(.CourseWeak$.GetName$())
For .CourseWeak$ {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
Module GetCourse {
Read .CourseWeak$
}
Class:
Module Student {
Read .StudentName$
}
}
Class Student {
Private:
StudentName$
CourseWeak$
Public:
Module Info {
Print "StudentName "; .StudentName$
If .CourseWeak$<>"" Then {
Print "Course:"
Rem "One Way": Print "Course:"; Eval$(.CourseWeak$.GetName$())
For .CourseWeak$ {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
Module GetCourse {
Read .CourseWeak$
}
Class:
Module Student {
Read .StudentName$
}
}
Class Course {
Private:
Name$
Id$
Public:
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
S1=Student("Babis")
C1=Course("Java","1214")
S1.GetCourse &C1
S1.Info
Dim Courses(20)
Courses(1)=Course("M2000","1417")
S1.GetCourse Weak$(Courses(1))
S1.Info
As we see, we can pass reference to array item (using Weak$() function). So now we thing that this isn't a good way to link two objects;
We Think that using an array pointer is a better way. A variable which point to an array is an idea
Rules for Arrays and Pointers to Arrays
In M2000 the statement A=(1,2,3) make an array with 3 items and link to A. A statement Link A to A() give to A() a link to A so we can use Print A(1) to get 2. A statement: Dim B() and another: B()=A() get a copy of A() to B(), so if at the left hand of assignment we have this kind of array A() we get a copy. So A is a pointer to array, B() is an array and we can export a pointer: A=B(). Also we can copy new values B()=((1,2),(3,4)), and we get Print B(0)(0)=1 , but we have to Dim B() first or make a Link to B().
\\ Student
Class Student {
Private:
StudentName$
CourseHolder=(,)
CourseRef=1
Public:
Module Info {
Print "StudentName "; .StudentName$
If Len(.CourseHolder)>.CourseRef-1 Then {
link .CourseHolder to Courses()
Print "Course:"
For Courses(.CourseRef) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
Module GetCourse {
Read .CourseHolder, .CourseRef
}
Class:
Module Student {
Read .StudentName$
}
}
Class Student {
Private:
StudentName$
CourseHolder=(,)
CourseRef=1
Public:
Module Info {
Print "StudentName "; .StudentName$
If Len(.CourseHolder)>.CourseRef-1 Then {
link .CourseHolder to Courses()
Print "Course:"
For Courses(.CourseRef) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
Module GetCourse {
Read .CourseHolder, .CourseRef
}
Class:
Module Student {
Read .StudentName$
}
}
Class Course {
Private:
Name$
Id$
Public:
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
Dim Courses(20)
Courses(1)=Course("M2000","1417")
M=Courses()
S1=Student("Babis")
S1.GetCourse M, 1
S1.Info
\\ Student
Class Student {
Private:
StudentName$
Inventory CourseHolder
CourseID$
Public:
Module Info {
Print "StudentName "; .StudentName$
If .CourseID$<>"" Then {
Print "Course:"
For .CourseHolder(.CourseID$) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
Module GetCourse {
Read .CourseID$
}
Class:
Module Student {
Read .CourseHolder, .StudentName$
}
}
Class Course {
Private:
Name$
Id$
Public:
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
Class Student {
Private:
StudentName$
Inventory CourseHolder
CourseID$
Public:
Module Info {
Print "StudentName "; .StudentName$
If .CourseID$<>"" Then {
Print "Course:"
For .CourseHolder(.CourseID$) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
Module GetCourse {
Read .CourseID$
}
Class:
Module Student {
Read .CourseHolder, .StudentName$
}
}
Class Course {
Private:
Name$
Id$
Public:
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
Inventory Courses
Append Courses "1417":=Course("M2000","1417")
S1=Student(Courses, "Babis")
S1.GetCourse "1417"
S1.Info
So now we want to make second Inventory for Students:
\\ Student
Class Student {
Private:
StudentName$
StudentID$="9999"
Inventory CourseHolder
CourseID$
Public:
Module Info {
Print "StudentName "; .StudentName$
If .CourseID$<>"" Then {
Print "Course:"
For .CourseHolder(.CourseID$) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
Module GetCourse {
Read .CourseID$
}
Class:
Module Student {
Read .CourseHolder, .StudentName$
Read ? .StudentID$
}
}
Class Course {
Private:
Name$
Id$
Public:
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
Class Student {
Private:
StudentName$
StudentID$="9999"
Inventory CourseHolder
CourseID$
Public:
Module Info {
Print "StudentName "; .StudentName$
If .CourseID$<>"" Then {
Print "Course:"
For .CourseHolder(.CourseID$) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
Module GetCourse {
Read .CourseID$
}
Class:
Module Student {
Read .CourseHolder, .StudentName$
Read ? .StudentID$
}
}
Class Course {
Private:
Name$
Id$
Public:
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
Inventory Courses, Students
Append Courses "1417":=Course("M2000","1417")
Append Students "1234":=Student(Courses, "Babis", "1234")
For Students("1234") {
.GetCourse "1417"
.Info
}
But
here is a better code, we can place more than one course in a student
\\
Student
Class Student {
Private:
StudentName$
StudentID$="9999"
Inventory CourseHolder, CourseIDs
Public:
Module Info {
Print "StudentName "; .StudentName$
If Len(.CourseIDs)>0 Then {
Print "Course:"
CourseID=Each(.CourseIDs)
While CourseID {
For .CourseHolder(Eval$(CourseID)) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
}
Function GetCourse {
While not empty {
Read CourseID$
If Not exist(.CourseIDs, CourseID$ ) Then {
Append .CourseIDs, CourseID$
=true \\ this in not an exit
}
}
}
Class:
Module Student {
Read .CourseHolder, .StudentName$
Read ? .StudentID$
}
}
Class Course {
Private:
Name$
Id$
Public:
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
Class Student {
Private:
StudentName$
StudentID$="9999"
Inventory CourseHolder, CourseIDs
Public:
Module Info {
Print "StudentName "; .StudentName$
If Len(.CourseIDs)>0 Then {
Print "Course:"
CourseID=Each(.CourseIDs)
While CourseID {
For .CourseHolder(Eval$(CourseID)) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
}
Function GetCourse {
While not empty {
Read CourseID$
If Not exist(.CourseIDs, CourseID$ ) Then {
Append .CourseIDs, CourseID$
=true \\ this in not an exit
}
}
}
Class:
Module Student {
Read .CourseHolder, .StudentName$
Read ? .StudentID$
}
}
Class Course {
Private:
Name$
Id$
Public:
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
Inventory Courses, Students
Append Courses "1417":=Course("M2000","1417"), "1517":=Course("Java","1517")
Append Students "1234":=Student(Courses, "Babis", "1234")
For Students("1234") {
If .GetCourse( "1417", "1517") Then .Info
}
Finally we make Courses to hold inventories with students too
\\ Student
Class Student {
Private:
StudentName$
StudentID$="9999"
Inventory CourseHolder, CourseIDs
Public:
Module SimpleInfo {
Print .StudentID$;": "; .StudentName$
}
Module Info {
Print .StudentID$;" : "; .StudentName$
If Len(.CourseIDs)>0 Then {
Print "Course:"
CourseID=Each(.CourseIDs)
\\ CourseID is an object above CourseIDs, which add a cursor
\\ we can read cursor as CourseID^
While CourseID {
\\ normaly we use Eval$(CourseID!) (look !)
\\ but here inventory CourseIDs has keys as data too.
\\ we can use .CourseHolder(CourseID^!)
\\ For .CourseHolder(Eval$(CourseID))
\\ For .CourseHolder(CourseID^!)
For .CourseHolder(Eval$(CourseID!)) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
}
Function GetCourse {
While not empty {
Read CourseID$
if Exist( .CourseHolder, CourseID$) Then {
If Not exist(.CourseIDs, CourseID$ ) Then {
Append .CourseIDs, CourseID$
For .CourseHolder(CourseID$) {
.PlaceStudent This.StudentID$
}
=true \\ this in not an exit
}
} Else {
=false
Break
}
}
}
Class:
Module Student {
Read .CourseHolder, .StudentName$
Read ? .StudentID$
}
}
Class Course {
Private:
Name$
Id$
Inventory StudentHolder
Inventory StudentList
Public:
Module PlaceStudent {
read StudentID$
If Not Exist(.StudentList, StudentID$) Then Append .StudentList, StudentID$
}
Module Info {
Print .Id$;" Students in Course ";.Name$
M=Each(.StudentList)
While M {
For .StudentHolder(Eval$(M)) {
.SimpleInfo
}
}
}
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .StudentHolder, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
Inventory Students, Courses
Append Courses "1417":=Course("M2000",Students, "1417"), "1517":=Course("Java",Students, "1517")
Append Students "1234":=Student(Courses, "Babis", "1234")
Append Students "1235":=Student(Courses, "Fotis", "1235")
For Students("1234") {
If .GetCourse( "1417", "1517") Then .Info
}
For Students("1235") {
If .GetCourse( "1517") Then .Info
}
M=Each(Courses)
While M {
\\ M^ is the counter and ! is a mark to place in Inventory counter and not key
\\ We can use Eval$(M!) which return the current key
For Courses(M^!) {
.Info
}
}
Class Student {
Private:
StudentName$
StudentID$="9999"
Inventory CourseHolder, CourseIDs
Public:
Module SimpleInfo {
Print .StudentID$;": "; .StudentName$
}
Module Info {
Print .StudentID$;" : "; .StudentName$
If Len(.CourseIDs)>0 Then {
Print "Course:"
CourseID=Each(.CourseIDs)
\\ CourseID is an object above CourseIDs, which add a cursor
\\ we can read cursor as CourseID^
While CourseID {
\\ normaly we use Eval$(CourseID!) (look !)
\\ but here inventory CourseIDs has keys as data too.
\\ we can use .CourseHolder(CourseID^!)
\\ For .CourseHolder(Eval$(CourseID))
\\ For .CourseHolder(CourseID^!)
For .CourseHolder(Eval$(CourseID!)) {
Print "","Name:";.GetName$()
Print "","ID:";.GetID$()
}
}
}
}
Function GetCourse {
While not empty {
Read CourseID$
if Exist( .CourseHolder, CourseID$) Then {
If Not exist(.CourseIDs, CourseID$ ) Then {
Append .CourseIDs, CourseID$
For .CourseHolder(CourseID$) {
.PlaceStudent This.StudentID$
}
=true \\ this in not an exit
}
} Else {
=false
Break
}
}
}
Class:
Module Student {
Read .CourseHolder, .StudentName$
Read ? .StudentID$
}
}
Class Course {
Private:
Name$
Id$
Inventory StudentHolder
Inventory StudentList
Public:
Module PlaceStudent {
read StudentID$
If Not Exist(.StudentList, StudentID$) Then Append .StudentList, StudentID$
}
Module Info {
Print .Id$;" Students in Course ";.Name$
M=Each(.StudentList)
While M {
For .StudentHolder(Eval$(M)) {
.SimpleInfo
}
}
}
Function GetName$ {
=.Name$
}
Function GetID$ {
=.ID$
}
Class:
Module Course {
Read .Name$, .StudentHolder, .ID$
If .Name$="" then Error "Undefined name for Course"
If Len(.ID$)<4 Then Error "Course's ID need at least 4 digits"
If not .ID$ ~ "[1-9]"+String$("[0-9]", Len(.ID$)-1) then Error "Invalid chars for Course's ID"
}
}
Inventory Students, Courses
Append Courses "1417":=Course("M2000",Students, "1417"), "1517":=Course("Java",Students, "1517")
Append Students "1234":=Student(Courses, "Babis", "1234")
Append Students "1235":=Student(Courses, "Fotis", "1235")
For Students("1234") {
If .GetCourse( "1417", "1517") Then .Info
}
For Students("1235") {
If .GetCourse( "1517") Then .Info
}
M=Each(Courses)
While M {
\\ M^ is the counter and ! is a mark to place in Inventory counter and not key
\\ We can use Eval$(M!) which return the current key
For Courses(M^!) {
.Info
}
}
As we can see we get a double linked objects, from arrays of objects (Garbage collection is automatic in M2000), without use of a "second" reference to any object of type Group. Garbage collector works only for objects like Arrays, Inventories and Stacks.
Five years later and version 12 execute same code without problem.
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου
You can feel free to write any suggestion, or idea on the subject.