Εμφάνιση αναρτήσεων με ετικέτα Προγραμματισμός. Εμφάνιση όλων των αναρτήσεων
Εμφάνιση αναρτήσεων με ετικέτα Προγραμματισμός. Εμφάνιση όλων των αναρτήσεων

Κυριακή 15 Μαρτίου 2020

Open a form after closing from unload button

This is an example of how we use a form, here with a handler as SIMPLE, to pass it to three modules, Inner, Inner1 and Inner2. Each module open the form, and when we close it using the unload button on the header of the form we make a fake unload using the Unload event, which handle the visible property to hide the form.

The Inner module get a copy of the window handler. This copy can't be used to make a linked property.

Why that happen ? Is the way M2000 interpreter internal window manager work. Only one real reference to a form exist. This means that when the module CheckIt exit, this reference released, and the form unloaded. The same done when we place a Nothing. When we click on unload button, an unload happen except we cancel it using the true as value to the first by reference variable at unload event. All events called in the same module we make the form and they have the name of the declaration, here the SIMPLE, here the simple.unload function is the service routine for the event. The call is a local type, which means that inside code in function Sumple.Unload we have the visibility of module checkit, so we can see the visible property.

So in Inner module we open the form as modal, using the m2000 internal window manager for modal windows. When we click on unload button, the modal loop exit, but the form not unload, but hide.

When we call the Inner1 we pass the Simple by reference, so we can make a visible property. Now we use a simple loop on visible property to make it like a modal (but a modal type to show also lock the under the form forms).

When we call the Inner2 we pass the Simple and the Visible both by reference.

Although the name of form inside Inner, Inner1 and Inner2 is That, the actual form has the name Simple and a prefix, as a weak reference to use it to call events. Also has a list for those events that aren't servising. This list erased and make it again if the form loose the focus.

If we make a fault in code, the module's stop working and all the variables erased, so the form unload, because the form's handler always is one. The visible property never hold a reference to form, but a reference to where Simple reference, the real reference which is only one.


Module CheckIt {
      \\ Simple is a first class object
      Declare Simple Form
      \\ we can define form before open
      Layer Simple {
            \\ center Window with 12pt font, 12000 twips width and 6000 twips height
            \\ ; at the end command to center the form in current screen
            Window 12, 12000, 6000;
            \\ make layer gray and split screen 0
            Cls #333333, 0
            \\   set split screen to 3rd line, like Cls ,2 without clear screen
            Scroll Split 2
            Cursor 0, 2
      }
      With Simple, "Title", "Hello Form", "visible" as visible
      \\ Without handling the unload event, a form unload for good
      \\ So we have to cancel the unload event and make the form non visible
      Function Simple.Unload {
            Read New &cancel
            visible=False
            cancel=true
      }
      Function Simple.Click {
            Layer Simple {
                  \\ open msgbox
                  Print Ask("Hello World")
                  Refresh
            }
      }
      Module Inner (That) {
            \\ open as modal  - also we handle the Unload event
            Method That, "Show", 1
      }
      Module Inner1 (&That) {
            \\ we can produce a linked property if we pass by reference
            \\ so &That make That same as Simple
            \\ if we pass by value we get an error "Can't Get Property"
            With That, "visible" as visible
            Method That, "Show"
            do
                  wait 1
            until not visible
      }
      Module Inner2 (&That, &thatToo) {
            Method That, "Show"
            do
                  wait 1
            until not thatToo
      }
      Inner Simple
      wait 1000
      Print "ok"

      Refresh
      Inner1 &Simple
      wait 1000
      Print "ok"

      Refresh
      Inner2 &Simple, &visible
      \\ now form deleted
      \\ Make the Simple as Nothing the form unloaded if not unloaded,
      \\ without an unload event.
      Declare Simple Nothing
}
CheckIt

Κυριακή 8 Μαρτίου 2020

Δένδρο του Πυθαγόρα (Αναδρομή)

Ένα πρόγραμμα που δείχνει πως λειτουργεί η αναδρομή με τον αλγόριθμο που φτιάχνει το δένδρο του Πυθαγόρα. Κάθε τετράγωνο αν δεν είναι φύλλο έχει άλλα δυο τετράγωνα μικρότερα που σχηματίζουν τρίγωνο στη μια πλευρά του.

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

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

Για την εμφάνιση του δένδρου χρησιμοποιούμε δυο εντολές, την Θέση που δέχεται απόλυτη τιμή καρτεσιανών συντεταγμένων (η πάνω αριστερή γωνία είναι το 0,0 και το ύψος αυξάνει προς τα κάτω οπότε η κάτω δεξιά γωνία είναι η χ.σημεία-πλάτος.σημείου, υ.σημεία-ύψος.σημείου). Οι μονάδες είναι τα twips, και ένα pixel έχει πλάτος όσο το πλάτος.σημείου και ύψος όσο το ύψος.σημείου. Τα 1440 twips είναι μια λογική ίντσα στην οθόνη και μια κανονική στο χαρτί του εκτυπωτή. Η λογική ίντσα λέγεται έτσι γιατί ένα μόνιτορ μπορεί να αλλάζει το πραγματικό μήκος της, αλλά και η θέση μας από αυτήν αλλάζει το μέγεθος επίσης.

Το πρόγραμμα ρυθμίζει τα χρώματα φόντου και γραμμών/κειμένου, σε Ματζέντα και Κίτρινο.
Μπορούμε να δώσουμε html χρώματα όπου το #FF0000 είναι το κόκκινο, η με την Χρώμα(255, 0,0).

Επίσης ενεργοποιεί την χάραξη γραμμών με anti-aliasing μια διαδικασία που είναι αργότερη από την απλή χάραξη αλλά ομαλοποιεί τα σκαλοπάτια που δημιουργούν οι πλάγιες γραμμές.

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

Για να βγει το δένδρο στη μέση της οθόνης (όπως φαίνεται στην εικόνα παρακάτω) κάνουμε μερικούς υπολογισμούς για το πρώτο δένδρο και κατόπιν το καλούμε.


ΤΜΗΜΑ Δένδρο_Πυθαγόρα {
 ΟΘΟΝΗ 5, 0  ' ΜΑΤΖΕΝΤΑ, ΧΩΡΙΣ ΧΩΡΙΣΤΗ ΟΘΟΝΗ
 ΠΕΝΑ 14   ' ΚΙΤΡΙΝΟ
 ΟΜΑΛΑ ΝΑΙ  ' χρήση GDI+ για ομαλοποίηση στις γραμμές
 
 ΣΤΗ π =Χ.ΣΗΜΕΙΑ , υ = π * 11 ΔΙΑ 16
 ΣΤΗ π2 = π ΔΙΑ 2, διαφορά = π ΔΙΑ 12
 ΣΤΗ ΤάξηΔένδρου = 6
 Δένδρο_Πυθαγόρα(π2 - διαφορά, υ-10, π2 + διαφορά, υ-10, 0)
 
 ΡΟΥΤΙΝΑ Δένδρο_Πυθαγόρα(χ1, ψ1, χ2, ψ2, βάθος)
 
     ΑΝ βάθος > ΤάξηΔένδρου ΤΟΤΕ ΕΞΟΔΟΣ ΡΟΥΤΙΝΑΣ
 
     ΤΟΠΙΚΗ δχ = χ2 - χ1, δψ = ψ1 - ψ2
     ΤΟΠΙΚΗ χ3 = χ2 - δψ, ψ3 = ψ2 - δχ
     ΤΟΠΙΚΗ χ4 = χ1 - δψ, ψ4 = ψ1 - δχ
     ΤΟΠΙΚΗ χ5 = χ4 + (δχ - δψ) / 2
     ΤΟΠΙΚΗ ψ5 = ψ4 - (δχ + δψ) / 2
     ΘΕΣΗ χ1, ψ1
     ΧΑΡΑΞΕ ΕΩΣ χ2, ψ2
     ΧΑΡΑΞΕ ΕΩΣ χ3, ψ3
     ΧΑΡΑΞΕ ΕΩΣ χ4, ψ4
     ΧΑΡΑΞΕ ΕΩΣ χ1, ψ1
     Δένδρο_Πυθαγόρα(χ4, ψ4, χ5, ψ5, βάθος+1)
     Δένδρο_Πυθαγόρα(χ5, ψ5, χ3, ψ3, βάθος+1)
 
 ΤΕΛΟΣ ΡΟΥΤΙΝΑΣ
}
Δένδρο_Πυθαγόρα


Παραλλαγή του προγράμματος με χρήση τμήματος αντί για ρουτίνα.
Τα τμήματα είναι πιο βαριές κατασκευές από τις ρουτίνες.

Για να καλέσουμε ένα τμήμα πρέπει να έχει δοθεί από πριν ο ορισμός του. Επίσης για να καλέσουμε ένα τμήμα με αναδρομή πρέπει να χρησιμοποιήσουμε τη ΚΑΛΕΣΕ,

Εδώ έχουμε το εσωτερικό τμήμα με το ίδιο όνομα με το εξωτερικό. Η πρώτη κλήση καλεί το εσωτερικό και η δεύτερη επειδή είναι με την ΚΑΛΕΣΕ καλεί το τρέχον.

Δείτε τις αλλαγές:
1. Το έξοδος δεν έχει τη λέξη ΡΟΥΤΙΝΑΣ
2. Οι μεταβλητές που θέλουμε να διαβάζονται σε κάθε κλήση αναδρομής πρέπει να είναι γενικές, γιατί εντός τμήματος θέαση υπάρχει μόνο για τοπικές του τμήματος και γενικές.

Οι γενικές που φτιάχτηκαν σε ένα τμήμα διαγράφονται στο πέρας εκτέλεσης αυτού. Αν καλούσαμε ένα τμήμα μέσα από αυτό που φτιάξαμε μια γενική έστω Α και σε αυτό φτιάξουμε μια νέα γενική Α τότε η δεύτερη θα ισχύει μέχρι το τμήμα που τη δημιούργησε τερματίσει και στην επιστροφή η γενική Α θα υπάρχει με την τιμή που φτιάχτηκε στο πρώτο τμήμα. Με αυτό το τρόπο "σκίασης" μεταβλητών μπορούμε να έχουμε τμήματα με γενικές μεταβλητές δικές του χωρίς να ενοχλεί υπάρχουσες γενικές. Επίσης για να αλλάξουμε τιμή σε μια γενική έχουμε το <= και όχι το =. Αν υπάρχει γενική Β και σε ένα τμήμα δώσουμε το Β=100 τότε η γενική σκιάζεται με τη τοπική Β. Οι τοπικές σκιάζουν τις γενικές και νεότερες γενικές σκιάζουν υπάρχουσες γενικές (όταν έχουμε ίδιο όνομα σε κάθε περίπτωση).


Οι κλήσεις τμημάτων περιορίζονται σε βάθος σε μερικές χιλιάδες. Σε αντίθεση η κλήση ρουτινών μπορεί να είναι σε βάθος σε εκατοντάδες χιλιάδες.

ΤΜΗΜΑ Δένδρο_Πυθαγόρα {
 ΟΘΟΝΗ 5, 0  ' ΜΑΤΖΕΝΤΑ, ΧΩΡΙΣ ΧΩΡΙΣΤΗ ΟΘΟΝΗ
 ΠΕΝΑ 14   ' ΚΙΤΡΙΝΟ
 ΟΜΑΛΑ ΝΑΙ  ' χρήση GDI+ για ομαλοποίηση στις γραμμές
 
 ΓΕΝΙΚΕΣ π =Χ.ΣΗΜΕΙΑ , υ = π * 11 ΔΙΑ 16
 ΓΕΝΙΚΕΣ π2 = π ΔΙΑ 2, διαφορά = π ΔΙΑ 12
 ΓΕΝΙΚΕΣ ΤάξηΔένδρου = 6
 ΤΜΗΜΑ Δένδρο_Πυθαγόρα(χ1, ψ1, χ2, ψ2, βάθος) {
 
     ΑΝ βάθος > ΤάξηΔένδρου ΤΟΤΕ ΕΞΟΔΟΣ
 
     ΣΤΗ δχ = χ2 - χ1, δψ = ψ1 - ψ2
     ΣΤΗ χ3 = χ2 - δψ, ψ3 = ψ2 - δχ
     ΣΤΗ χ4 = χ1 - δψ, ψ4 = ψ1 - δχ
     ΣΤΗ χ5 = χ4 + (δχ - δψ) / 2
     ΣΤΗ ψ5 = ψ4 - (δχ + δψ) / 2
     ΘΕΣΗ χ1, ψ1
     ΧΑΡΑΞΕ ΕΩΣ χ2, ψ2
     ΧΑΡΑΞΕ ΕΩΣ χ3, ψ3
     ΧΑΡΑΞΕ ΕΩΣ χ4, ψ4
     ΧΑΡΑΞΕ ΕΩΣ χ1, ψ1
     ΚΑΛΕΣΕ Δένδρο_Πυθαγόρα χ4, ψ4, χ5, ψ5, βάθος+1
     ΚΑΛΕΣΕ Δένδρο_Πυθαγόρα χ5, ψ5, χ3, ψ3, βάθος+1
 }
 Δένδρο_Πυθαγόρα π2 - διαφορά, υ-10, π2 + διαφορά, υ-10, 0
}
Δένδρο_Πυθαγόρα







Στο Info αρχείο που υπάρχει στην εγκατάσταση της γλώσσας υπάρχει το Pyth με αγγλικές εντολές το πρώτο πρόγραμμα καθώς και το Pyth1 επίσης με αγγλικές εντολές με εμφάνιση δυο δένδρων υπό γωνία το ένα αντικριστό από το άλλο:

Pyth1:

MODULE Pythagoras_Example{
 CLS 5, 0  ' MAGENTA, split line = 0  
 PEN 14  ' YELLOW
 \\ Linux smoothing not work (we can use the statement but without effect)
 IF ISWINE ELSE SMOOTH ON
 \\ PYTHAGORAS TREE
 \\ by definition all variables ar type of a double
 GLOBAL p=7, p4=PI/4, p2=PI/2, s2=SQRT(2)/2
 MODULE center_p (r, t){
  MODULE pythagoras_tree (r, dx, depth) {
   r2=r-p2
   DRAW ANGLE r, dx
   DRAW ANGLE r2, dx
   DRAW ANGLE r, -dx
   DRAW ANGLE r2, -dx
   IF depth>10 THEN EXIT
   s3=dx*s2
   depth++
   STEP ANGLE r+p4, s3*2
   CALL pythagoras_tree r-p4,  s3, depth
   STEP ANGLE r, -dx-s3
   STEP ANGLE r, s3
   STEP ANGLE r+p4, -s3
   CALL pythagoras_tree r+p4,  s3, depth
   STEP ANGLE r-p4, s3  
  } 
  MOVE SCALE.X/2, SCALE.Y/2 
  STEP ANGLE PI-p4+r, t*s2
  CALL pythagoras_tree r, t, 1
 }
 r=PI/3
 pixels=100
 center_p r, 100*TWIPSX
 center_p r+PI, 100*TWIPSX
 CopyImageToClipboard()
 
 Sub CopyImageToClipboard()
  LOCAL Scr$=""
  MOVE 0,0
  COPY SCALE.X, SCALE.Y TO Scr$
  CLIPBOARD Scr$
 END SUB
}
Pythagoras_Example



Σάββατο 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