Σε αυτή την αναθεώρηση έγιναν δυο αναβαθμίσεις
1. Συναρτήσεις για την χρήση Τεραδόνιων (Quaternions).
Είχα δώσει σε πρόγραμμα σε Μ2000 το τεραδόνιο, με αγγλικές εντολές στο RosettaCode.org
Σε αυτήν την αναβάθμιση έβαλα τις συναρτήσεις μέσα στη βιβλιοθήκη Math της M2000. Η διαφορά με τα παρακάτω προγράμματα είναι ότι η βιβλιοθήκη δουλεύει με δείκτες σε διαρθρώσεις μνήμης. Έτσι μπορούμε να έχουμε "μνήμες" με πίνακες από τεραδόνια και να εκτελούμε μαζικούς υπολογισμούς, απευθείας με ρουτίνες σε γλώσσα μηχανής. Βεβαίως και αυτά εδώ τα προγράμματα δεν χάνουν την αξία τους, γιατί έχουμε περισσότερη επαφή με το τι γίνεται και πώς. (αν και ο κώδικας της Μ2000 και της βιβλιοθήκης, που είναι ενσωματωμένη στο περιβάλλον, είναι σε Visual Basic, ωστόσο είναι για προχωρημένους προγραμματιστές πάνω στην γλώσσα VB6).
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)=Αληθής
}
Δοκίμασε
και με αγγλικές εντολές:
Output:
Παράδειγμα με τη χρήση των νέων συναρτήσεων της βιβλιοθήκης Math.
Επειδή η βιβλιοθήκη χρησιμοποιεί δείκτες πρέπει να δίνουμε δείκτες που εξάγουμε από κάποια Διάρθρωση μνήμης και όχι οτιδήποτε γιατί θα τερματίσει απότομα το περιβάλλον (κρασαρισμα). Γενικά οι διαρθρώσεις έχουν φτιαχτεί για να μην γίνεται αυτό, όμως η βιβλιοθήκη δεν ελέγχει τους δείκτες, και τους χρησιμοποιεί για μετακινήσεις στοιχείων. Αν δεν μπορεί να γράψει, ή αν δεν υπάρχει η διεύθυνση που δίνει ο δείκτης, τότε έχουμε "παραβίαση" άρα άμεσο σταμάτημα.
Αφού ορίσουμε το Math ως αντικείμενο Math, και αφού φτιάξουμε χώρο στη μνήμη για τα τεραδόνια, και ορίσουμε δείκτες στην μνήμη, καλούμε μεθόδους.
Ορισμένες μέθοδοι δέχονται προαιρετικά ορίσματα, και συνήθως είναι το τελευταίο. Πχ αν θέλουμε να προσθέσουμε δυο τεραδόνια, δίνουμε δυο δείκτες, το δείκτη του πρώτου και το δείκτη του δεύτερου, ή τρεις, δηλαδή επιπλέον τον δείκτη που θα πάει το αποτέλεσμα. Αν δεν δώσουμε ειδικά που θέλουμε το αποτέλεσμα θα πάει στο πρώτο.
Ορισμένες μέθοδοι έχουν στο όνομα το Mul (όχι το Mult) το οποίο δηλώνει ότι το πρώτο νούμερο θα είναι ένας αριθμος επαναλήψεων. Ουσιαστικά στους δείκτες που δίνουμε προσθέτει σε κάθε επανάληψη το μέγεθος του στοιχείου που δηλώνει ο δείκτης σε ψηφία μνήμης (bytes). Άρα πρέπει να δώσουμε τόσο όσο θα μείνει ο δείκτης εντός περιοχής τιμών.
Δείτε στο πρόγραμμα που ακολουθεί: Φτιάχνουμε μια διάρθρωση μνήμης (Buffer) με 200 quaternion (τεραδόνια). Έχουμε ορίσει τι θα περιέχουν (τέσσερις double). Η βιβλιοθήκη περιμένει να έχουμε 4 double. Επειδή δηλώνουμε Clear οι τιμές σε όλους τους double μηδενίζουν (η μνήμη που ορίζουμε στη διάρθρωση καθαρίζει με 0 σε κάθε ψηφίο της). Οι διαρθρώσεις είναι αντικείμενα, τα οποία περιέχουν φυσική μνήμη. Το Quat(3) θα γυρίσει την διεύθυνση μνήμης του τέταρτου τεραδόνιου.
Για βοήθεια έχουμε μερικές λάμδα συναρτήσεις, οι οποίες μας εξυπηρετούν γιατί κρατούν το δείκτη στο αντικείμενο Διάρθρωση.
Δείτε ότι δεν διαγράφουμε την βιβλιοθήκη. Ειδικά η Math δεν διαγράφεται, απλά ο δείκτης σε αυτήν μπορεί να διαγραφεί. Μπορούμε να ορίζουμε την Math τοπικά όπου θέλουμε και πάλι το ίδιο αντικείμενο θα μας δώσει (δεν βγάζει νέο). Θα μπορούσε κανείς να φτιάξει μια δεύτερη βιβλιοθήκη με ίδιες συναρτήσεις κατ όνομα, και να δουλεύει το πρόγραμμα με μια αλλαγή στην γραμμή που ορίζουμε την Math (ώστε να οριστεί σε κάποια εξωτερική).
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
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") Τότε {
Διάβασε .α,.β,.γ,.δ
.ΥπολόγισεΜ
}
}
}
Κλάση Τεραδόνιο {
α,β,γ,δ
Ιδιότητα Τιμή$ {
Αξία {
Ένωσε γονικό α,β,γ,δ στη α,β,γ,δ
Αξία$=Μορφή$("{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
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
\\ 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
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου
You can feel free to write any suggestion, or idea on the subject.