Κυριακή, 19 Αυγούστου 2018

Αναθεώρηση 6 Έκδοση 9.4

Ένα μικρό λάθος διορθώθηκε, το οποίο απέτρεπε την σωστή αντιγραφή ενός δείκτη σε ομάδα σε μια νέα θέση στο σωρό τιμών.

Ακολουθεί ένα μικρό πρόγραμμα που επεξηγεί τις δυο μορφές των ομάδων (Group), ως επώνυμα (named), και ως επιπλέοντα (float). Η ομάδα που "επιπλέει" είναι ανώνυμη, δεν έχει δηλαδή θέση αυτή και τα μέλη της στο σύστημα ονομάτων της Μ2000, αλλά έχει θέση σε κάτι άλλο, όπως σε ένα πίνακα, σε μια κατάσταση ειδών, σε ένα σωρό τιμών.
Μια ομάδα δημιουργείται ως επώνυμη, και όταν την επιστρέψουμε από μια συνάρτηση τότε επιστρέφουμε μια επιπλέουσα (που μετακινείται) ομάδα που έχει μέλη αντίγραφα των μελών της αρχικής. Αν την δώσουμε σε ένα πίνακα τότε αυτή πιάνει τη θέση ως έχει (αν ήδη υπάρχει στην θέση αυτή κάτι άλλο το πετάει), ενώ αν την δώσουμε σε νέο όνομα τότε δημιουργείται ένα επώνυμο αντικείμενο με πρότυπο αυτό που του δώσαμε, αν δώσουμε σε υπάρχουσα επώνυμη ομάδα τότε έχουμε συγχώνευση, ώστε αυτό που δίνουμε να υπάρχει στην επώνυμη ομάδα με ότι έχει αυτή παραπάνω.

Ένας δείκτης σε ομάδα δείχνει σε μια επώνυμη ή σε μια επιπλέουσα ή είναι μηδενικός (παραμένει όμως να έχει τύπο Ομάδα). Αν ο δείκτης δείχνει σε επώνυμη ομάδα στην ουσία έχει ισχνή αναφορά σε αυτήν. Με συνέπεια αν μετακινηθεί ο δείκτης σε χώρο όπου έχει πια διαγραφεί αυτό που δείχνει να έχει άκυρη ισχνή αναφορά, και η οποία θα φανεί με το πρώτη ζήτηση. Αν ο δείκτης δείχνει σε επιπλέουσα ομάδα τότε την κρατάει "ζωντανή", γιατί υπάρχει αρίθμηση στο πόσοι δείκτες την έχουν, και για να διαλυθεί πρέπει να φύγει και ο τελευταίος. Οι δείκτες αντιγράφονται με = ενώ για να πάρουν νέο δείκτη από μια ομάδα πρέπει να δώσουμε αυτό Α->Α(3) όπου σημαίνει ότι ο Α έχει δείκτη σε επιπλέουσα ομάδα (δεν είναι επώνυμη), ή Α->(Αλφα)  όπου οι παρενθέσεις στο όνομα κάνουν το Αλφα να αντιγραφεί σε επιπλέουσα ομάδα, και το Α->Αλφα το οποίο είναι δείκτης σε επώνυμη ομάδα.


Module Big {
      Module Checkit {
            Group Alfa {
                  \\ all are public by default
                  x=-10, y=30, a$="a String"
                  Module testMe {
                        Print .x, .y, .a$
                  }
            }
            \\ show list  Alfa[Group], Alfa.x, Alfa.y, Alfa.a$
            List
            \\ Show Alfa.testMe
            Modules ?
            Alfa.testme
            Dim a(1 to 3)
            A(1)=Alfa
            \\ A(1) has a float group, a copy of Alfa
            A(1).x+=20
            Print A(1).x=10 ' true
            Print Alfa.x=-10 ' true
           
            \\ thin arrow used instead = to get pointer from a group
            \\ notice that A(2)->(Alfa) make a copy before return a pointer
            A(2)->Alfa
            \\ A(2) has a pointer to Alfa (a pointer to a named group is a weak reference, not adding ref count to Alfa)
            \\ we can use this pointer until this module exit, and Alfa erased
            A(2).x+=20
            Print A(2).x=10 ' true
            Print Alfa.x=-10 ' false, it is 10, A(2) has a pointer  to Alfa
            Group Alfa {
                  Function AddXY {
                        =.x+.y
                  }
            }
            Print A(2).AddXY()=40
            \\ A(1) has no AddXY() function
            Print Valid(A(1)..AddXY())=false
            \\ z is a pointer to A(1)
            \\ a pointer to a float group is actual a pointer to object, adding a ref count.
            \\ we can use this pointer anytime because hold object alive
            \\ using z->0& we pass a null, and release the object. This happen at the exit of this module
            \\ but before the exit maybe we tranfer a copy to another destination, like a container, or in current stack of values
            z->A(1)
            Module CallByRef (&a) {
                  a+=1000
            }
            For A(1), A(2), alfa {
                  \\ any new in this block destroyed at exit
                  \\ A(2) points to Alfa, and Alfa exist as named group
                  \\ So only A(1) close all
                  Print .x, ..x, ..x ' 10 10 10
                  ..x+=30
                  Print ..x=40, ...x=40 ' A(2) points to alfa
                  \\ we pass .x (from A(1)) as reference, because .x has a name (hidden), it is linked now
                  CallByRef &.x
                  \\ z points to a float group A(1), but now  A(1 )is linked and so z find link and get proper value
                  \\ we use fat arrow to access member from a pointer (as a variable)
                  \\ for arrays not need to use fat arrow
                  Print z=>x = 1010
            }
            \\ so now we want to upgrade A(1)
            For A(1) {
                   m=Alfa ' we get a copy
                   m=This ' we get a merge
                  This=M ' we get a merge in reverse
            }
            \\ Now M not exist.
            Print A(1).AddXY()
            Push z
       }
      Flush ' empty stack here       
       \\ call checkit
       Checkit
       \\ Print stack items from current stack
       Stack
       \\ we see a  *[Group] means a pointer to Group
       \\ we want Here to read Good as a group (not as a pointer)
       pGroup=StackItem()
       Read Good as group
       \\ Good is a pointer
       Print Good.AddXY() , pGroup=>AddXY()
       pGroup->0& ' release object
       List
       Modules ?
}
Big
\\ There are no variables
List
\\ There is one module Big
Modules ?




Επιπλέον μπήκαν νέες σελίδες στην βοήθεια για τελεστές. Υπάρχει η σελίδα Τελεστές, με βοήθεια τελεστές βγαίνει, αλλά τώρα προστέθηκαν και ξεχωριστές σελίδες για τους τελεστές, στη λίστα ΔΙΕΡΜΗΝΕΥΤΗΣ
Η ΚΑΙ ΑΠΟ ΕΙΝΑΙ ΔΙΑ ΔΙΑ# ΥΠΟΛ ΥΠΟΛ#

Βοήθεια Διερμηνευτής

ή μπορούμε να δώσουμε χωριστά
βοήθεια υπολ
βοήθεια "υπολ#"   (θέλει εισαγωγικά για το #

πχ στο Δια# υπάρχει το παράδειγμα

α=-20
β=6
\\ Ευκλείδεια διαίρεση και υπόλοιπο
γ=α Διά# β
δ=α Υπόλ# β
Τύπωσε γ, δ  ' -4   4
Τύπωσε α=β*γ+δ
\\ Κανονική διαίρεση και υπόλοιπο
γ=α Διά β
δ=α Υπόλ β
Τύπωσε γ, δ ' -3  -2
Τύπωσε α=β*γ+δ

και στα αγγλικά αυτό:
a=-20
b=6
\\ Euclidean  div as div#, mod as mod#
c=a div# b
d=a mod# b
Print c, d  ' -4   4
Print a=b*c+d
\\ normal
c=a div b
d=a mod b
Print c, d ' -3  -2
Print a=b*c+d

Σάββατο, 18 Αυγούστου 2018

Αναθεώρηση 5 κδοση 9.4

Σε αυτή την αναθεώρηση έγιναν δυο αναβαθμίσεις

1. Συναρτήσεις για την χρήση Τεραδόνιων (Quaternions).
Είχα δώσει σε πρόγραμμα σε Μ2000 το τεραδόνιο, με αγγλικές εντολές στο RosettaCode.org
Σε αυτήν την αναβάθμιση έβαλα τις συναρτήσεις μέσα στη βιβλιοθήκη Math της M2000. Η διαφορά με τα παρακάτω προγράμματα είναι ότι η βιβλιοθήκη δουλεύει με δείκτες σε διαρθρώσεις μνήμης. Έτσι μπορούμε να έχουμε "μνήμες" με πίνακες από τεραδόνια και να εκτελούμε μαζικούς υπολογισμούς, απευθείας με ρουτίνες σε γλώσσα μηχανής. Βεβαίως και αυτά εδώ τα προγράμματα δεν χάνουν την αξία τους, γιατί έχουμε περισσότερη επαφή με το τι γίνεται και πώς. (αν και ο κώδικας της Μ2000 και της βιβλιοθήκης, που είναι ενσωματωμένη στο περιβάλλον, είναι σε Visual Basic, ωστόσο είναι για προχωρημένους προγραμματιστές πάνω στην γλώσσα VB6).


Τμήμα Δοκίμασε {
      Κλάση Τεραδόνιο {

            α,β,γ,δ
            Ιδιότητα Τιμή$ {
                  Αξία {
                      Ένωσε γονικό α,β,γ,δ στη α,β,γ,δ
                       Αξία$=Μορφή$("{0} + {1}i + {2}j + {3}k",α,β,γ,δ)
                  }
            }
            Ιδιότητα Μέγεθος { Αξία}
            Τελεστής "==" {
                  Διάβασε ν
                  Βάλε ==ν.α και ==ν.β και ==ν.γ και ==ν.δ
            }
            Τμήμα ΥπολόγισεΜ {
                  .[Μέγεθος]<=ρίζα(**2+**2+**2+**2)
             }
            Τελεστής Μοναδιαίος {  ' είναι το - πριν το αντικείμενο
                  -! : -! : -! :-!
            }
            Συνάρτηση Συζ {
                  τ=Αυτό
                  Για τ {
                         -! : -! :-!
                  }
                  =τ
            }
            Συνάρτηση Πρόσθεσε {
                  τ=Αυτό
                  Για τ {
                         +=Αριθμός : .ΥπολόγισεΜ
                  }
                  =τ
            }
            Τελεστής "+"  {
                  Διάβασε τ2
                  Για Αυτό, τ2 {
                        +=..α :+=..β:+=..γ:+=..δ
                        .ΥπολόγισεΜ
                  }
            }
            Συνάρτηση Πολλαπλασίασε(r) {
                  τ=Αυτό
                  Για τ {
                        *=r:*=r:*=r:*=r:.ΥπολόγισεΜ
                  }
                  =τ
            }
            Τελεστής "*" {
                  Διάβασε τ2
                  Για Αυτό, τ2 {
                        Βάλε *..α-*..β-*..γ-*..δ
                        Βάλε *..β+*..α+*..δ-*..γ
                        Βάλε *..γ-*..δ+*..α+*..β
                        <=*..δ+*..γ-*..β+*..α
                        Διάβασε , ,
                        .ΥπολόγισεΜ
                  }
            }      
            Κλάση:
            Τμήμα Τεραδόνιο {
                  Άν Ταύτιση("NNNN") Τότε {
                        Διάβασε ,,,
                       .ΥπολόγισεΜ
                  }
            }
      }

      r=7
      τ=Τεραδόνιο(1,2,3,4)
      τ1=Τεραδόνιο(2,3,4,5)
      τ2=Τεραδόνιο(3,4,5,6)

 
      τ_αρνητικό=-τ
      τ_συζυγή=τ.συζ()
      τ_πολλαπλάσιο=τ.πολλαπλασίασε(r)
      τ_επαυξημένο=τ.Πρόσθεσε(r)
      τ1τ2=τ1*τ2
      τ2τ1=τ2*τ1

      Τύπωσε "τ = ";τ.Τιμή$
      Τύπωσε "Μέγεθος τ = ";τ.Μέγεθος
      Τύπωσε "Αρνητικό τ = ";τ_αρνητικό.Τιμή$
      Τύπωσε "Συζυγή τ = ";τ_συζυγή.Τιμή$
      Τύπωσε "Πολλαπλασίασε τ 7 = ";τ_πολλαπλάσιο.Τιμή$
      Τύπωσε "Πρόσθεσε τ 7 = ";τ_επαυξημένο.Τιμή$
      Τύπωσε "τ1 = ";τ1.Τιμή$
      Τύπωσε "τ2 = ";τ2.Τιμή$
      Τύπωσε "τ1 * τ2 = ";τ1τ2.Τιμή$
      Τύπωσε "τ2 * τ1 = ";τ2τ1.Τιμή$
      Τύπωσε τ1==τ1 ' Αληθής
      Τύπωσε τ1τ2==τ2τ1 ' Ψευδής

      Τύπωσε (τ1 * τ2 == τ2 * τ1)=Ψευδής
      Τύπωσε (τ1 * τ2 == τ1 * τ2)=Αληθής
}
Δοκίμασε

και με αγγλικές εντολές:


Module CheckIt {
      class Quaternion {
            \\ by default are double
            a,b,c,d
            Property ToString$ {
                  Value {
                      link parent a,b,c, d to a,b,c,d
                       value$=format$("{0} + {1}i + {2}j + {3}k",a,b,c,d)
                  }
            }
            Property Norm { Value}
            Operator "==" {
                  read n
                  push .a==n.a and .b==n.b and .c==n.c and .d==n.d
            }
            Module CalcNorm {
                  .[Norm]<=sqrt(.a**2+.b**2+.c**2+.d**2)
             }
            Operator Unary {
                  .a-! : .b-! : .c-! :.d-!
            }
            Function Conj {
                  q=this
                  for q {
                         .b-! : .c-! :.d-!
                  }
                  =q
            }
            Function Add {
                  q=this
                  for q {
                         .a+=Number : .CalcNorm
                  }
                  =q
            }
            Operator "+"  {
                  Read q2
                  For this, q2 {
                        .a+=..a :.b+=..b:.c+=..c:.d+=..d
                        .CalcNorm
                  }
            }
            Function Mul(r) {
                  q=this
                  for q {
                        .a*=r:.b*=r:.c*=r:.d*=r:.CalcNorm
                  }
                  =q
            }
            Operator "*" {
                  Read q2
                  For This, q2 {
                        Push .a*..a-.b*..b-.c*..c-.d*..d
                        Push .a*..b+.b*..a+.c*..d-.d*..c
                        Push .a*..c-.b*..d+.c*..a+.d*..b
                        .d<=.a*..d+.b*..c-.c*..b+.d*..a
                        Read .c, .b, .a
                        .CalcNorm
                  }
            }      
            class:
            module Quaternion {
                  if match("NNNN") then {
                        Read .a,.b,.c,.d
                       .CalcNorm
                  }
            }
      }
      \\ variables
      r=7
      q=Quaternion(1,2,3,4)
      q1=Quaternion(2,3,4,5)
      q2=Quaternion(3,4,5,6)

      \\ perform negate, conjugate, multiply by real, add a real, multiply quanterions, multiply in reverse order
      qneg=-q
      qconj=q.conj()
      qmul=q.Mul(r)
      qadd=q.Add(r)
      q1q2=q1*q2
      q2q1=q2*q1

      Print "q = ";q.ToString$
      Print "Normal q = ";q.Norm
      Print "Neg q = ";qneg.ToString$
      Print "Conj q = ";qconj.ToString$
      Print "Mul q 7 = ";qmul.ToString$
      Print "Add q 7 = ";qadd.ToString$
      Print "q1 = ";q1.ToString$
      Print "q2 = ";q2.ToString$
      Print "q1 * q2 = ";q1q2.ToString$
      Print "q2 * q1 = ";q2q1.ToString$
      Print q1==q1 ' true
      Print q1q2==q2q1 ' false
      \\ multiplication and equality in one expression
      Print (q1 * q2 == q2 * q1)=false
      Print (q1 * q2 == q1 * q2)=True
}
CheckIt


Output:
q = 1 + 2i + 3j + 4k
Normal q = 5.47722557505166
Neg q = -1 + -2i + -3j + -4k
Conj q = 1 + -2i + -3j + -4k            
Mul q 7 = 7 + 14i + 21j + 28k
Add q 7 = 8 + 2i + 3j + 4k
q1 = 2 + 3i + 4j + 5k
q2 = 3 + 4i + 5j + 6k
q1 * q2 = -56 + 16i + 24j + 26k
q2 * q1 = -56 + 18i + 20j + 28k
     True
    False
     True
     True



Παράδειγμα με τη χρήση των νέων συναρτήσεων της βιβλιοθήκης Math.
Επειδή η βιβλιοθήκη χρησιμοποιεί δείκτες πρέπει να δίνουμε δείκτες που εξάγουμε από κάποια Διάρθρωση μνήμης και όχι οτιδήποτε γιατί θα τερματίσει απότομα το περιβάλλον (κρασαρισμα). Γενικά οι διαρθρώσεις έχουν φτιαχτεί για να μην γίνεται αυτό, όμως η βιβλιοθήκη δεν ελέγχει τους δείκτες, και τους χρησιμοποιεί για μετακινήσεις στοιχείων. Αν δεν μπορεί να γράψει, ή αν δεν υπάρχει η διεύθυνση που δίνει ο δείκτης, τότε έχουμε "παραβίαση" άρα άμεσο σταμάτημα.
Αφού ορίσουμε το Math ως αντικείμενο Math, και αφού φτιάξουμε χώρο στη μνήμη για τα τεραδόνια, και ορίσουμε δείκτες στην μνήμη, καλούμε μεθόδους.
Ορισμένες μέθοδοι δέχονται προαιρετικά ορίσματα, και συνήθως είναι το τελευταίο. Πχ αν θέλουμε να προσθέσουμε δυο τεραδόνια, δίνουμε δυο δείκτες, το δείκτη του πρώτου και το δείκτη του δεύτερου, ή τρεις, δηλαδή επιπλέον τον δείκτη που θα πάει το αποτέλεσμα. Αν δεν δώσουμε ειδικά που θέλουμε το αποτέλεσμα θα πάει στο πρώτο.

Ορισμένες μέθοδοι έχουν στο όνομα το Mul (όχι το Mult) το οποίο δηλώνει ότι το πρώτο νούμερο θα είναι ένας αριθμος επαναλήψεων. Ουσιαστικά στους δείκτες που δίνουμε προσθέτει σε κάθε επανάληψη το μέγεθος του στοιχείου που δηλώνει ο δείκτης σε ψηφία μνήμης (bytes). Άρα πρέπει να δώσουμε τόσο όσο θα μείνει ο δείκτης εντός περιοχής τιμών.

Δείτε στο πρόγραμμα που ακολουθεί: Φτιάχνουμε μια διάρθρωση μνήμης (Buffer) με 200 quaternion (τεραδόνια). Έχουμε ορίσει τι θα περιέχουν (τέσσερις double). Η βιβλιοθήκη περιμένει να έχουμε 4 double. Επειδή δηλώνουμε Clear οι τιμές σε όλους τους double μηδενίζουν (η μνήμη που ορίζουμε στη διάρθρωση καθαρίζει με 0 σε κάθε ψηφίο της). Οι διαρθρώσεις είναι αντικείμενα, τα οποία περιέχουν φυσική μνήμη. Το Quat(3) θα γυρίσει την διεύθυνση μνήμης του τέταρτου τεραδόνιου.
Για βοήθεια έχουμε μερικές λάμδα συναρτήσεις, οι οποίες μας εξυπηρετούν γιατί κρατούν το δείκτη στο αντικείμενο Διάρθρωση.
Δείτε ότι δεν διαγράφουμε την βιβλιοθήκη. Ειδικά η Math δεν διαγράφεται, απλά ο δείκτης σε αυτήν μπορεί να διαγραφεί. Μπορούμε να ορίζουμε την Math τοπικά όπου θέλουμε και πάλι το ίδιο αντικείμενο θα μας δώσει (δεν βγάζει νέο). Θα μπορούσε κανείς να φτιάξει μια δεύτερη βιβλιοθήκη με ίδιες συναρτήσεις κατ όνομα, και να δουλεύει το πρόγραμμα με μια αλλαγή στην γραμμή που ορίζουμε την Math (ώστε να οριστεί σε κάποια εξωτερική).



Module Checkit {
      \\ Math  Library Version 1.1
      \\ Math library is internal in M2000
      Declare Math Math
      With math, "Version" as Math.version
      Print "Math Version:", Math.Version
      structure Quaternion {
             w as double
             x as double
             y as double
             z as double
      }
      \\ A Buffer is a memory block
      Buffer Clear Quat as Quaternion*200
      Buffer Clear Doubles as Double*3
      \\ quaternions are  4x Doubles
      \\ get address to variables, so q is a pointer
      q=Quat(0)
      q1=Quat(1)
      q2=Quat(2)
      result=Quat(3)
      q1q2=Quat(4)
      q2q1=Quat(5)
      arr3=Quat(6)
      r=Doubles(0) ' this used for double
      \\ We use lambda with closure a pointer to Quat (a memory buffer)
      Quat.ToString$=lambda$ Quat (addr) ->{
            where=addr-Quat(0) ' we use it a byte offset (because of using as double)
            =format$("{0} + {1}i + {2}j + {3}k",Eval(Quat, where as double), Eval(Quat, where+8 as double), Eval(Quat, where+16 as double),Eval(Quat, where+24 as double))
      }
      PokeReal = lambda Doubles (addr, val) ->{
            where=addr-Doubles(0)
            Return Doubles, 0!where:= val as double
      }
      Real.ToString$=lambda$ Doubles (addr) -> {
            =format$("{0}",eval(Doubles, addr-Doubles(0) as double))
      }
      Call PokeReal(r, 7)
      Method Math, "Quaternion", q, 1,2,3,4
      Method Math, "Quaternion", q1, 2,3,4,5
      Method Math, "Quaternion", q2, 3,4,5,6
      Print "q = ";Quat.ToString$(q)
      Method Math, "QuatNormRetDouble", q as q.norm
      Print "Normal q = "; q.norm
      Method Math, "QuatNeg", q, result
      Print "Neg q = ";Quat.ToString$(result)
      Method Math, "QuatConj", q, result
      Print "Conj q = ";Quat.ToString$(result)
      Method Math, "QuatMultReal", q, r, result
      Print "Mul q 7 = ";Quat.ToString$(result)
      \\ r is pointer to double
      \\Method Math, "QuatAddReal", q, r, result
      \\ there is another way to pass value
      Method Math, "QuatAddByReal", q, 7, result
      Print "Add q 7 = ";Quat.ToString$(result)
      Print "q1 = ";Quat.ToString$(q1)
      Print "q2 = ";Quat.ToString$(q2)
      Method Math, "QuatMultQuat", q1, q2, q1q2
      Print "q1 * q2 =";Quat.ToString$(q1q2)
      Method Math, "QuatMultQuat", q2, q1, q2q1
      Print "q2 * q1 =";Quat.ToString$(q2q1)
      Print Real.ToString$(r)
      \\ using standard output (#0.0000), decimal point defined from locale)
      Locale 1033
      Method Math, "QuatString", q2q1 as q2q1.str$
      Print q2q1.str$
      Locale 1032
      Method Math, "QuatString", q2q1 as q2q1.str$
      Print q2q1.str$
      Method Math, "QuatString", q2q1, "#0.00"  as q2q1.str$
      Print "q2 * q1 =("+q2q1.str$+")"
      \\ using 6th quaternion from Quat (base 0, so 6th has index 5)
      Print eval(Quat, 5!w), eval(Quat, 5!x), eval(Quat, 5!y), eval(Quat, 5!z)
      Print "q = ";Quat.ToString$(q)
      Print "q1 = ";Quat.ToString$(q1)
      Print "q2 = ";Quat.ToString$(q2)
      Method Math, "QuatAddRealMulConst", 3, q, r
      Print "q = ";Quat.ToString$(q)
      Print "q1 = ";Quat.ToString$(q1)
      Print "q2 = ";Quat.ToString$(q2)
      Call PokeReal(r+8, -7)
      Call PokeReal(r+16, -14)
      \\ use an array of doubles
      Method Math, "QuatAddRealMulMul", 3, q, r
      Print "q = ";Quat.ToString$(q)
      Print "q1 = ";Quat.ToString$(q1)
      Print "q2 = ";Quat.ToString$(q2)
      \\ q=q*q, q1=q1*q1, q2=q2*q2
      Method Math, "QuatMultQuatMul", 3, q, q
      Print "q = ";Quat.ToString$(q)
      Print "q1 = ";Quat.ToString$(q1)
      Print "q2 = ";Quat.ToString$(q2)
      \\ Arr3=q*q, Arr3+32=q1*q1, Arr3+64=q2*q2
      Method Math, "QuatMultQuatMul", 3, q, q, Arr3
      Print "Arr3(0) = ";Quat.ToString$(Arr3)
      Print "Arr3(1)= ";Quat.ToString$(Arr3+32)
      Print "Arr3(2) = ";Quat.ToString$(Arr3+64)
}
Checkit





2. Αναβαθμίστηκε η γλώσσα σχετικά με τα νήματα, σε σχέδιο ταυτόχρονο, ή concurrent. Υπήρχε αυτό το σχέδιο μαζί με το διαδοχικό ή sequential, από την 5η έκδοση της γλώσσας (εδώ και τέσσερις εκδόσεις), αλλά δεν είχε προβλεφθεί ένα ζήτημα, το οποίο για να εξηγηθεί χρειάζεται μια εισαγωγή περί νημάτων, μικρή ωστόσο.

Τα νήματα είναι μέρη τμήματος που εκτελούνται σε δικό τους αντικείμενο εκτέλεσης, έχουν δικό τους σωρό τιμών αλλά "βλέπουν" οτιδήποτε βλέπει ο κώδικας του τμήματος. Επιπλέον τα νήματα μπορούν να έχουν δικές τους στατικές μεταβλητές, ενώ δεν βλέπουν τις στατικές μεταβλητές που πιθανόν έχει το τμήμα που ανήκουν. Γενικά αποφεύγουμε τη δημιουργία μεταβλητών μέσα σε νήμα (ενδέχεται από άλλη λειτουργία να διαγραφεί). Όμως μπορούμε από νήματα να καλούμε συναρτήσεις, τμήματα ακόμα και ρουτίνες. Κάθε φορά ότι δημιουργεί το τμήμα που καλούμε το καθαρίζει στην επιστροφή, οπότε αν στο μεταξύ ένα άλλο νήμα δημιουργήσει μεταβλητή τότε θα διαγραφεί και αυτή!
Με το διαδοχικό σχέδιο, ο διαχειριστής νημάτων εκτελεί το κάθε νήμα χωριστά. Κάθε νήμα οφείλει να είναι μικρό. Δεν βάζουμε επαναλήψεις σε ένα νήμα, γιατί το κάθε νήμα εκτελείται σε επανάληψη χρονικά. Υπάρχουν εντολές που σταματάμε ένα νήμα, που το ξεκινάμε πάλι, που το διαγράφουμε, καθώς και εντολή εκτέλεσης από άλλο νήμα στο νήμα που θέλουμε (με συνέπεια να δώσουμε τιμές σε στατικές ή να βάλουμε τιμές στο σωρό του τμήματος). Επίσης υπάρχει εντολή που βάζει το χρονικό διάστημα επανεκτέλεσης, το οποίο δεν μπορεί να είναι μικρότερο από αυτό που καθορίζουμε αλλά ενδέχεται να είναι μεγαλύτερο. Μιλάμε για το χρόνο που ξεκινάει ένα νήμα. Αν το έχουμε υπολογίσει καλά θα πρέπει κάθε νήμα να ξεκινάει σε τακτά διαστήματα. Ενδέχεται να έχουμε σε ένα νήμα μια καθυστέρηση επειδή ζητάμε μια εισαγωγή τιμής. Σε διάφορες καθυστερήσεις αφήνει ο διαχειριστής να εκτελούνται άλλα νήματα. Δηλαδή το "διαδοχικό" σχέδιο δεν αποκλείει την εκτέλεση νήματος όταν ένα νήμα εκτελείται.

Το ταυτόχρονο σχέδιο, εκτελεί ένα νήμα στο κανονικό χρόνο, και μετά την εκτέλεση μιας εντολής δίνει την εκτέλεση σε άλλο νήμα, αν το άλλο είναι σε φάση εκτέλεσης, ή χρειάζεται να εκκινήσει. Αν οι εντολές βρίσκονται σε μπλοκ {} τότε εκτελούνται όλες μαζί. Ορισμένες φορές σε δομές Αν και Επίλεξε θα πρέπει να είναι σε μπλοκ { }. Μέχρι να βελτιωθεί ο διερμηνευτής, θα ισχύει αυτό.
Δείτε τώρα ποιο ήταν το θέμα και έτσι έγινε αναβάθμιση. 'Οταν δυο νήματα τρέχουν "ταυτόχρονα" και τα δυο νήματα καλούν την ίδια συνάρτηση ή τμήμα και το πρώτο ήδη την εκτελεί, και βρίσκεται σε αναμονή πατήματος πλήκτρου (θα μπορούσε να ήταν και άλλου είδους), τότε η δεύτερη εκτέλεση του τμήματος σημαινει "επανείσοδος" του τμήματος, χωρίς να έχει γίνει μέσα από το τμήμα (χωρίς αναδρομή). Εδώ έχουμε κλήση τμήματος από το ίδιο τμήμα (αφού το νήμα είναι κώδικας του τμήματος που φτιάχτηκε) δεύτερη φορά, χωρίς να έχει τερματίσει η πρώτη.

Ο διερμηνευτής φορτώνει το κώδικα που θα εκτελέσει για ένα τμήμα σε ένα αντικείμενο εκτέλεσης προς "κατανάλωση". Οι μεταβλητές όμως βρίσκονται σε ένα από δυο σημεία: Οι κανονικές σε μια δομή με συνάρτηση κατακερματισμού για γρήγορη εύρεση σε χρόνο Ο(1) (ουσιαστικά το μέγεθος του ονόματος προκαλεί την όποια διαφορά, γιατί η συνάρτηση κατακερμαρισμού χρειάζεται περισσότερο χρόνο για να εξάγει αποτέλεσμα). Αυτή η δομή είναι γενική και έχει τρεις ιδιότητες: Διαγράφει ανάποδα (από το τελευταίο). Δέχεται όμοια κλειδιά, Δίνει πάντα το τελευταίο όμοιο. Το άλλο σημείο είναι ο χώρος στατικών μεταβλητών. Αυτός πρόσκαιρα είναι σε μια συλλογή ιδιωτική στο αντικείμενο εκτέλεσης. Δεν παίρνει όλους τύπους μεταβλητών και δεν δίνει αναφορές σε μεταβλητές (δεν μπορούμε να πάρουμε αναφορά σε μεταβλητή και να την δώσουμε με το & σε κλήση). Μπορούμε όμως να έχουμε δείκτες πίνακες. Όταν τερματίσει το αντικείμενο εκτέλεσης τότε παραδίδει την συλλογή στατικών (αν έχει) και αυτή καταχωρείται στην συλλογή στατικών του πατρικού αντικείμενου εκτέλεσης. Σε όποιο σντικείμενο εκτέλεσης δώσουμε την Καθαρό ή Clear καθαρίζουμε τις μεταβλητές και τις στατικές από το αντικείμενο αυτό και για όποια άλλα τμήματα/συναρτήσεις έχουμε καλέσει από αυτό. Οι λάμδα συναρτήσεις δεν έχουν στατικές, αντί αυτών λειτουργούν με κλεισίματα.

Σε αυτήν την αναβάθμιση για τα αντικείμενα που ξεκινούν από νήματα, μπήκε ο αριθμός νήματος ως στοιχείο του κρυφού ονόματος του τμήματος ή συνάρτησης όπως γνωρίζει το αντικείμενο εκτέλεσης, και βάσει αυτού οι στατικές ξεχωρίζουν (το ίδιο τμήμα έχει διαφορετικές στατικές για κάθε νήμα που το καλεί), και επίσης στις κανονικές μεταβλητές έχουμε επίσης διαφορά στο ουσιαστικό όνομα (ενώ στο κώδικα το βλέπουμε ίδιο), και έτσι δεν ταιριάζουν τα ονόματα. Στο αρχικό πρόβλημα όταν καλούσαμε το τμήμα που ήδη έτρεχε και σε αυτό ζητάγαμε μαι μεταβλητή, τότε την έβρισκε ήδη και δεν έφτιαχνε νέα, αλλά έγραφε στην "παλιά" η οποία ήταν από το ίδιο τμήμα που όμως έτρεχε για άλλο νήμα.

Ακολοθούν τα προγράμματα ελέγχου (δοκιμάστε τα σε παλαιότερη έκδοση).
Ο κώδικας είναι χωρίς χρώμα επίτηδες. Χρησιμοποιούμε το αυτόματο νήμα After ή Μετά το οποίο τρέχει μια φορά μετά το χρόνο που δίνουμε
Η λειτουργία γίνεται με τον διερμηνευτή στην εξορισμού ρύθμιση secure names ("+sec"). Αν την αλλάξουμε σε "-sec" θα δούμε το λάθος. Το λάθος βγαίνει στο z=m, όπου στο a$=key$ έχει κληθεί πάλι το beta και έχει αλλάξει η τιμή του m. Με το παλιό διερμηνευτή (υπάρχει για μέγιστη συμβατότητα), σε ορισμένες περιπτώσεις δεν υπάρχει δευτερο σετ στατικών, το x δηλαδή εμφανίζεται ίδιο και στο beta που καλείται από άλλο νήμα.

Σε κάθε Beta καλούμε το ίδιο αναδρομικά, μέχρι να αδειάσει ο σωρός τιμών (στον οποίο έχουμε βάλει τρια αλφαριθμητικά). Κάθε νήμα έχει το δικό του σωρό τιμών.

Κλήση τμημάτων με όνομα
thread.plan concurrent
set switches "+sec"
clear
module beta(m, k$) {
      static x=10, z=m
    
      if x=8 then a$=key$
      print z=m, x, k$, module.name$, module$
      x--
      if not empty then call beta, z
    
}
after 300 {
      beta 1, "a1", "b1", "c1"   
}
after 200 {
     beta 2,  "a", "b", "c"
}
wait 1000


Κλήση τμημάτων με την Κάλεσε (Call)
thread.plan concurrent
set switches "+sec"
clear
function beta(m, k$) {
      static x=10, z=m
     
      if x=9 then a$=key$
      print z=m, x, k$, module.name$, module$
      x--
      if not empty then call beta(z)
     
}
after 300 {
      call beta( 1, "a1", "b1", "c1" )
}
after 200 {
     call beta( 2,  "a", "b", "c")
}
wait 1000


Δοκιμάστε το και όταν ο κώδικας αυτός περιέχεται σε εσωτερικό τμήμα:
module checkit {
      thread.plan concurrent
      set switches "+sec"
      clear
      module beta(m, k$) {
            static x=10, z=m
            if x=8 then a$=key$
            print z=m, x, k$, module.name$, module$
            x--
            if not empty then call beta, z
      }
      after 300 {
       call   beta 1, "a1", "b1", "c1"    
      }
      after 200 {
        call   beta 2,  "a", "b", "c"
      }
      wait 1000
}
checkit




Κλήση συνάρτησης ως τμήμα με την Κάλεσε (Call), εδώ η συνάρτηση παίρνει το σωρό τιμών του νήματος.
thread.plan concurrent
set switches "+sec"
clear
function beta(m, k$) {
      static x=10, z=m
     
      if x=9 then a$=key$
      print z=m, x, k$, module.name$, module$
      x--
      if not empty then call beta(z)
     
}
after 300 {
      call beta( 1, "a1", "b1", "c1" )
}
after 200 {
     call beta( 2,  "a", "b", "c")
}
wait 1000


Από εξωτερικό τμήμα:

module checkit {
      thread.plan concurrent
      set switches "+sec"
      clear
      function beta(m, k$) {
            static x=10, z=m
           
            if x=9 then a$=key$
            print z=m, x, k$, module.name$, module$
            x--
            if not empty then call beta(z)
           
      }
      after 300 {
            call beta( 1, "a1", "b1", "c1" )
      }
      after 200 {
           call beta( 2,  "a", "b", "c")
      }
      wait 1000
     
}
call checkit 



Κλήση συνάρτησης σε  έκφραση, εδώ περνάμε το σωρό του νήματος ως παράμετρο (ουσιαστικά καθαρίζουμε το σωρό του νήματος δίνοντάς τον στη συνάρτηση). Οι συναρτήσεις όταν καλούνται από εκφράσεις έχουν δικό τους σωρό τιμών.

thread.plan concurrent
      set switches "+sec"
      clear
      function beta(m, k$) {
            static x=10, z=m
           
            if x=9 then a$=key$
            print z=m, x, k$, module.name$, module$
            x--
            =x
            if not empty then =beta(z, ![])+x
                       
      }
      after 300 {
            m=beta( 1, "a1", "b1", "c1" )
      }
      after 200 {
           m=beta( 2,  "a", "b", "c")
      }
      wait 1000


Και από εσωτερικό τμήμα:
module checkit {
      thread.plan concurrent
            set switches "+sec"
            clear
            function beta(m, k$) {
                  static x=10, z=m
                 
                  if x=9 then a$=key$
                  print z=m, x, k$, module.name$, module$
                  x--
                  =x
                  if not empty then =beta(z, ![])+x
                             
            }
            after 300 {
                  m=beta( 1, "a1", "b1", "c1" )
            }
            after 200 {
                 m=beta( 2,  "a", "b", "c")
            }
            wait 1000   
}
checkit


Κλήση λάμδα συνάρτησης (Εδώ δεν έχουμε στατικές, αλλά κλεισίματα). Επίσης δείται ότι έχουμε βγάλει αντίγραφο για να καλούμε από το άλλο τμήμα, διαφορετικά κάθε νήμα θα καλούσε την ίδια λάμδα (με τα ίδια κλεισίματα). Τα κλεισίματα αντιγράφονται, και αν έχουμε δείκτες τότε έχουμε αντιγραφή δείκτη (άρα η νέα και η παλιά λάμδα θα βλέπουν το ίδιο αντικείμενο). Πίνακες με παρενθέσεις (δεν είναι δείκτες σε πίνακες), ομάδες, γεγονότα και λάμδα αντικείμνα μπαίνουν με αντιγραφή.


thread.plan concurrent
      set switches "+sec"
      clear
      beta =lambda X=10, z=-1 (m, k$) -> {
               if z=-1 then z=m
           
            if x=9 then a$=key$
            print z=m, x, k$, module.name$, module$
            x--
            =x
            if not empty then =lambda(z, ![])+x
                       
      }
      beta1=beta
      after 300 {
            m=beta( 1, "a1", "b1", "c1" )
      }
      after 200 {
           m=beta1( 2,  "a", "b", "c")
      }
      wait 1000


Και από εσωτερικό τμήμα

module checkit {
thread.plan concurrent
      set switches "+sec"
      clear
      beta =lambda X=10, z=-1 (m, k$) -> {
               if z=-1 then z=m
           
            if x=9 then a$=key$
            print z=m, x, k$, module.name$, module$
            x--
            =x
            if not empty then =lambda(z, ![])+x
                       
      }
      beta1=beta
      after 300 {
            m=beta( 1, "a1", "b1", "c1" )
      }
      after 200 {
           m=beta1( 2,  "a", "b", "c")
      }
      wait 1000

}
checkit