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

Αναθεώρηση 2 Έκδοση 9,4

Διορθώθηκε ένα λάθος που προέκυψε λόγο μετακίνησης κώδικα! Στο νέο χώρο υπήρχε μια δήλωση πίνακα (ο οποίος δεν χρησιμοποιούταν πουθενά, προφανώς από αντιγραφή γραμμών από άλλο σημείο) και η οποία απέτρεπε την εντολή Print να δει αν ένα αρχείο στο οποίο θα τύπωνε ήταν για Unicode ή για Ansi χαρακτήρες, και το έβλεπε συνέχεια σαν Ansi. Ευτυχώς έχω μια σειρά προγράμματα που ελέγχω την γλώσσα. Και έτσι έπιασα το λάθος! Απλά δεν το είχα εκτελέσει όταν ανέβασα την αναθεώρηση 1.


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

172 Tasks in RosettaCode for M2000


Tasks




Τρίτη, 7 Αυγούστου 2018

Έκδοση 9.4

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

Το ερώτημα που προκύπτει είναι: Τι θα γίνει με τα αντικείμενα που δεν μπορούν να διαγραφούν;
Η απάντηση είναι ότι αυτά τα αντικείμενα θα κρατάνε μνήμη, για όσο το περιβάλλον της Μ2000 τρέχει. Με τον τερματισμό όλη η μνήμη αποδίδεται στο σύστημα.

Ο άλλος τρόπος είναι να διαγράψουμε τα σημεία που εν γνώση μας ξέρουμε ότι μπορεί να κρατούν αναφορές "κυκλικές".


Το παρακάτω πρόγραμμα (απλή συνδεδεμένη λίστα με Κατάσταση (inventory) μετά από τις δυο επαναλήψεις του Α κόλλαγε στις προηγούμενες εκδόσεις (λόγω του συλλέκτη σκουπιδιών).
Παρόλο που κάθε Inventory (κατάσταση) δείχνει κάποιο άλλο, στο τέλος όλα καθαρίζουν, επειδή καθαρίζουν βάσει εκείνου που προηγείται. Κανονικά θα έπρεπε να διαγράψουμε κάθε στοιχείο από την συνδεδεμένη λίστα, πριν την πετάξουμε!



Module Checkit {
      Module A {
            Profiler
            Function Node {
                        read val
                        inventory alfa="data":=val
                        =alfa
                  }
            Global Inventory Null
            Module SetNext {
                  Read Where, next
                  If exist(Where, "Next") Then {
                  Return m,"Next":=next
                  } Else Append Where, "Next":=next
            }
            Module SetNextNull {
                  Read Where, next
                  If exist(Where, "Next") Then Delete Where, "Next"
            }
            Function GetNext {
                Read Where
                If exist(Where, "Next") then {
                       =Eval(Where)
                } Else ok=false : =Null
            }
            Function Peek {
                  read Where
                  =where("data")
            }
            Module Poke {
                  Read Where, val
                  Return Where, "data":=val
            }
            Function RemoveNode(Where) {
                   If exist(Where, "Next") then {=Eval(Where) : Clear Where} Else =Null
                  
            }
            Stack New {
                  For I=100 to 109 {
                        Head=Node(i)
                        Print "Push:"; I
                         If Not Empty Then SetNext Head ' second from stack
                         Push Head
                  }
            }
            M=Head
            do {
                  Print "Pop i";Peek(M)
                  poke M, peek(M)+1000
                  Print "Pop New i";Peek(M)
                  M=GetNext(M)
                   If M is Null then exit
            } always
            While Not Head is Null {
                  Head=RemoveNode(Head)
            }
            Print Timecount
      }
      FOR I=1 TO 100
      A
      NEXT I
}
Checkit




Ένας άλλος τρόπος για να κάνουμε μια μια συνδεδεμέμη λίστα είναι να χρησιμοποιήσουμε Διάρθρωση μνήμης (Biffer) βάσει μιας Δομής (Structure).
Δεν μπορούμε στη Μ2000 σε μια διάρθρωση Μνήμης να γράψουμε τη διεύθυνση ενός αντικειμένου*. Δεν μπορούμε να γράψουμε την διεύθυνση της Διάθρωσης στο  next της alfa. Όμως η διάρθρωση έχει κάτι μοναδικό, την διεύθυνση του πρώτου στοιχείου της. Έτσι μπορούμε να γράψουμε αυτό και βάσει αυτού να βρίσκουμε με μια Κατάσταση της διάρθρωση.
Στο A έχουμε τα Profiler και Timecount, το πρώτο ξεκινάει την χρονομέτρηση και το δεύτερο μας επιστρέφει το χρόνο. Το προηγούμενο πρόγραμμα που χρησιμοποιεί συνδεδεμένες Καταστάσεις είναι δυο φορές πιο γρήγορο, από αυτό που χρησιμοποιεί συνδεδεμένες διαρθρώσεις και μια κατάσταση ως μεσολαβητής για επιστροφή διάρθρωσης για δοσμένη διεύθυνση (το προηγούμενο δεν το χρειάζεται αυτό γιατί το στοιχείο "Next" γυρίζει άμεσα το δείκτη της κατάσταση σε ένα όνομα (το οποίο είναι και αυτό κατάσταση και δείχνει το ίδιο αντικείμενο).



Module Checkit {
      Module A {
            Profiler
            Structure alfa {
                  data as long
                  next as long
            }
            Node=Lambda alfa -> {
                  Buffer Clear A1 as alfa
                  if isnum then read x as long : Return A1, 0!data:=x
                  =A1
            }
            Class Mem {
                  Inventory Pointers
                  ' we can change iPoke, iPeek, iPeek$ to suit with structure
                  iPoke=lambda (m, data) -> {Return m, 0!data:=data}
                  iPeek=lambda (m)->Eval(m, 0!data)
                  iPeek$=lambda$ (m)->Str$(Eval(m, 0!data),"")
                  Module Final Poke (where) {
                        Call .iPoke(.Pointers(where(0))) ' two parameters
                  }
                  Function Final Peek(where) {
                        =.iPeek(.Pointers(where(0)))
                  }
                  Function Final Peek$(where) {
                        =.iPeek$(.Pointers(where(0)))
                  }
                  Function Final SetSmart {
                        ok=false
                        while not empty {
                            read what
                            Try ok {
                               Append .Pointers, what(0):=what
                              }
                              if not ok then exit
                        }
                        =ok
                  }
                  Module Final SetNext(where, next) {
                        m=.Pointers(where(0))
                        \\ ? where(0), next(0)
                        Return m, 0!next:=next(0)
                  }
                  Module Final SetNextNull(where) {
                        m=.Pointers(where(0))
                        Return m, 0!next:=0
                  }
                  Function Final GetNext(where) {
                       m=.Pointers(where(0))
                       =Eval(m, 0!next)
                  }
                  Module Final Free(where) {
                        Try {
                        Delete .Pointers, where(0)
                        }      
                  }
                  Module final FlushMem {
                        Inventory .Pointers
                  }
            }
            Mem=Mem()
            \\ works Mem->Mem()  so Mem as a pointer to group
          
            For Mem {
                  Stack New {
                        For I=100 to 109 {
                             Head=Node(i)
                             Print "Push:"; I
                             If not .SetSmart(Head) then exit
                              If Not Empty Then .SetNext Head ' second from stack
                              Push Head
                        }
                        Flush
                  }
                  Print "Check List"
                  If len(.Pointers)>0 then {
                        M=Head
                        do {
                              Print "Pop i";.Peek(M)
                              .poke M, .peek(M)+1000
                              M1=.GetNext(M)
                               If M1=0 then exit
                              M=.Pointers(M1)
                        } always
                        M=Head
                        do {
                              Print "Pop i";.Peek(M)
                              M1=.GetNext(M)
                               If M1=0 then exit
                              M=.Pointers(M1)
                        } always
                  }
                  .FlushMem
            }
            Print TimeCount
      }
      FOR I=1 TO 100
      A
      NEXT I
}
Checkit


*Το πρόβλημα με τις διευθύνσεις μνήμης των αντικειμένων είναι ότι η "γυμνή" προσέγγισή τους μπορεί να ρίξει το πρόγραμμα. Πχ αν έχει γίνει μια αλλαγή στο χώρο που γράφεται το αντικείμενο, από  λάθος, τότε το αντικείμενο χάνει τη συνοχή του. Αυτό συμβαίνει γιατί στην αρχή κάθε αντικείμενο έχει μια διεπαφή, τουλάχιστον, και αυτή είναι ένας πίνακας με διευθύνσεις ρουτινών  (συναρτήσεων), με κώδικα σε γλώσσα μηχανής. Μπορεί κανείς να επιλέξει από μια λίστα διεπαφών, κάποια ρουτίνα. Ο λόγος που υπάρχουν πολλές λίστες είναι απλός. Κάθε λίστα έχει αριθμημένες τις ρουτίνες, έτσι όταν θέλουμε να καλέσουμε την 3η ρουτίνα της δεύτερης διεπαφής, τότε ο τρόπος εύρεσης είναι απλός, και οδηγεί με βεβαιότητα σε αυτήν που ψάχνουμε. Αν αύριο θέλουμε το αντικείμενο να υποστηρίζει μια άλλη διεπαφή, απλά ανανεώνουμε το αντικείμενο. Αν είχαμε μια μοναδική διεπαφή τότε κάθε νέα συνάρτηση θα έκανε την διεπαφή νέα. Προς το παρόν η Μ2000 λειτουργεί για αντικείμενα που ορίζει με την Όρισε (εξωτερικά), μόνο το Dispatch interface, το οποίο έχει την ιδιότητα για όποια αντικείμενα το υποστηρίζουν, να δίνει τον αριθμό συνάρτησης βάσει ονόματος (τα ονόματα στα αγγλικά, αν και το COM model παίρνει και σε άλλες γλώσσες, αλλά προς το παρόν δεν έχω δει να το χρησιμοποεί κανείς με άλλη γλώσσα εκτός από αγγλικά). Με αυτό τον τρόπο πετυχαίνει κανείς το late binding, δηλαδή να χρησιμοποιήσει ένα αντικείμενο, που πριν την εκκίνηση του προγράμματος δεν του ήταν γνωστό (αυτό συμβαίνει με την γλώσσα, όπου ορισμένα αντικείμενα όπως οι φόρμες, ενώ είναι εσωτερικά στο κώδικα του περιβάλλοντος, πρπσγγίζονται από την γλώσσα Μ2000 ως αντικείμενα εξωτερικά, και για το λόγο αυτό τα ονόματα των μεθόδων και των ιδιοτήτων δίνονται ως αλφαριθμητικά στα αγγλικά).
Αυτό που ψάχνω τελευταία είναι ο τρόπος χρήσης του IEnumVariant για να προσεγγίζω λίστες (collections) που επιστρέφουν ορισμένα εξωτερικά αντικείμενα (και δεν έχουν σχέση με τα collections της Visual Basic 6, εκτός από ένα κοινό, τη διεπαφή, η οποία είναι ίδια)











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

Αναθεώρηση 33 Έκδοση 9.3

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

Η εντολή αρχεία (files) μπορεί να ψάχνει έναν ή περισσότερους τύπους (βάζουμε το | μεταξύ τους) και εκείνα τα αρχεία με συγκεκριμένα αλφαριθμητικά. Παλαιότερα μπορούσαμε να δώσουμε ένα αλφαριθμητικό, αλλά τώρα μπορούμε με το | να δίνουμε μια λίστα, ώστε να μας βρει το αρχείο που σε μια τουλάχιστον γραμμή θα έχει όλα τα αλφαριθμητικά.
Αρχεία "gsb", "alfa|beta" ψάχνει και για τα δυο "alfa" και "beta" και μας επιστρέφει μια λίστα αρχείων.



Κυριακή, 29 Ιουλίου 2018

Αναθεώρηση 31 Έκδοση 9.3

Rosettacode.org tasks for M2000: 152

1. Σε αυτήν την αναθεώρηση διορθώθηκαν μερικές "κακοτεχνίες", της 9.3 (δεν υπήρχαν πριν). Επανήλθε η σωστή λειτουργία των Μέλος$() ή Member$() και Μέλους.Τύπος$() ή Member,type$() όταν έχουμε γενική ομάδα (global group), καθώς και το Μακρύς ή Long στη δήλωση μέλους μιας ομάδας (group)

global group alfa {
      Long x=10
      k=lambda->0
      a=(,)
      dim a%(10)
}
for i=1 to group.count(alfa)
      Print member$(alfa, i), member.type$(alfa, i)
next i

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

\\ πρόγραμμα εύρεσης του e ανάλογα με το τύπο που εισάγουμε 
Module E {
      Function e (n){
            \\ max 28 for decimal
           n=n/28: For i=27 to 1 :n=1+n/i: next i
           =n
      }
      Print e(1@);
      Print " Decimal"
      Print e(1);
      Print " Double"
      Print e(1~);
      Print " Float"
}
E


3. Προσθήκη στην Τιμή() ή Val().
Με αυτήν την προσθήκη μπορούμε να γνωρίζουμε πόσοι χαρακτήρες δίνουν τον αριθμό που γυρίζει. σε μια τρίτη παράμετρο που περνάει με αναφορά (δεν χρειάζεται το &, αλλά αν μπει δεν πειράζει). Αν γυρίσει -1 τότε σημαίνει ότι δεν βρήκε κανένα χαρακτήρα που να δείχνει αριθμό.
Η Τιμή("α") θα γυρίσει 0, αλλά το α δεν είναι αριθμός. Έστω μ μια μεταβλητή, το Τιμή("α",μ) θα γυρίσει το 0 και στο μ το -1. Το -1 επιλέχθηκε γιατί στο Τιμή("", μ) το 0 θα ήταν το ίδιο όσο το μήκος του αλφαριθμητικού και δεν μας βολεύει για τις παρακάτω συναρτήσεις (έχουν μπει στην βοήθεια για να μπορεί κανείς να τις αντιγράφει). Θυμίζω εδώ ότι τα Αληθές και Ψευδές δεν είναι τύπου Λογικός, αλλά στην μετατροπή γίνονται όπως πρέπει. Επίσης οι συγκρίσεις δίνουν πάντα τύπο λογικό, οπότε αν θέλουμε να δώσουμε την Ψευδές ως τύπος λογικός, και όχι το 0, τότε η Τιμή(0->Λογικός) τον επιστρέφει ως τύπο λογικό με τιμή FALSE.

Οι συναρτήσεις Αριθμός() και ΑκέραιοςΑριθμός() δίνουν λογικό τύπο. Το μόνο ενδιαφέρον για να πάρουμε το 0 ως FALSE είναι στην Τύπωσε, όπου μας δίνει τα Αληθές και Ψευδές αντί των -1 και 0, όταν βλέπει τύπο λογικό. Μάλιστα η Τύπωσε εμφανίζει τα Αληθές/True και Ψευδές/False αν έχουμε επιλέξει τον διακόπτη "+sbl" (ShowBooLean=sbl), με την διακόπτες "+sbl" στην κονσόλα, ή την Θέσε διακόπτες "+sbl" σε τμήμα/συνάρτηση. Μπορούμε να δούμε τη θέση του διακόπτη με την Monitor ή Έλεγχος στη κονσόλα (ή με τη χρήση της Set ή Θέσε, από τμήμα ή συνάρτηση, επειδή αυτή η τελευταία στέλνει την γραμμή ως έχει στον μεταφραστή εντολών της κονσόλας, που διαφέρει κάπως, και αυτός γνωρίζει τις Monitor και Switches).


με ελληνικές εντολές
Συνάρτηση Αριθμός(α$) {
      Κάνε β
      =Τιμή(Ψευδές->Λογικός)
      Δες {
            Αν ΕινΓρ Τότε {
                  z=Τιμή(α$,γράμμα$, β)
            } Αλλιώς.Αν ΕινΑρ Τότε {
                  z=Τιμή(α$,αριθμός, β)
            } Αλλιώς z=Τιμή(α$,"", β)
            =β>Μήκος(α$)
      }
}
Συνάρτηση ΑκέραιοςΑριθμός(α$) {
      Κάνε β
      =Τιμή(Ψευδές->Λογικός)
      Δες {
            z=Τιμή(α$,"Ακ", β)
            =β>Μήκος(α$)
      }
}
Τύπωσε ΑκέραιοςΑριθμός("1221213123213")=Αληθές
Τύπωσε ΑκέραιοςΑριθμός("1221213.123213")=Ψευδές
Τύπωσε Αριθμός("123131232131231231.23123123")=Αληθές
Τύπωσε Αριθμός("-123131232131231231.23123123e112")=Αληθές
Τύπωσε Αριθμός("-123131232131231231.23123123e112", ",")=Ψευδές
Τύπωσε Αριθμός("-123131232131231231.23123123e112", 1036)=Ψευδές
Τύπωσε Αριθμός("-123131232131231231.23123123e112", 1033)=Αληθές
Τύπωσε Τιμή("233.44sec", 1033)=233.44
α$="233.44sec"
β=0
Τύπωσε Τιμή(α$, 1033, β)=233.44
Αν β>0 Τότε Τύπωσε Mid$(α$, β)="sec"
\\ οποιοδήποτε αλφαριθμητικό με μήκος >1 για χαρακτήρα δεκαδικών εξαιρεί τα δεκαδικά.
Τύπωσε Τιμή(α$, "??", β)=233
Αν β>0 Τότε Τύπωσε Mid$(α$, β)=".44sec"



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

Function IsNumeric(a$) {
      def m
      =val(false->boolean)
      Try {
            if islet then {
                  z=val(a$,letter$, m)
            } else.if isnum then {
                  z=val(a$,number, m)
            } else z=val(a$,"", m)
            =m>len(a$)
      }
}
Function IsIntegerNumeric(a$) {
      def m
      =val(false->boolean)
      Try {
            z=val(a$,"Int", m)
            =m>len(a$)
      }
}
Print IsIntegerNumeric("1221213123213")=true
Print IsIntegerNumeric("1221213.123213")=false
Print isNumeric("123131232131231231.23123123")=true
Print isNumeric("-123131232131231231.23123123e112")=true
Print isNumeric("-123131232131231231.23123123e112", ",")=false
Print isNumeric("-123131232131231231.23123123e112", 1036)=false
Print isNumeric("-123131232131231231.23123123e112", 1033)=true
Print val("233.44sec", 1033)=233.44
a$="233.44sec"
m=0
Print val(a$, 1033, m)=233.44
if m>0 then Print Mid$(a$, m)="sec"
\\ any string for decimal point with len >1 cut decimals
Print val(a$, "??", m)=233
if m>0 then Print Mid$(a$, m)=".44sec"

Παρασκευή, 27 Ιουλίου 2018

Αναθεώρηση 30 (Έκδοση 9.3)

1. Διόρθωση στην εντολή Εισαγωγή για τις μεταβλητές με το % στο τέλος του ονόματος (ακέραιες, κατά όνομα, αλλά με διάφορους τύπους εσωτερικά).
2. Διόρθωση στα γραφικά στα τόξα και τομείς όταν επιλέγουμε το Ομαλά Ναι (εμφανίζονται βάσει του GDI+ των Windows). Παραμένει ένα ζήτημα: Στο GDI32 (το παλιό αλλά γρήγορο) όταν επιλέγουμε πχ πι/6 (pi/6) σε κύκλο μας δείχνει τις 30 μοίρες (30*6=180 δηλαδή ένα πι - ο κύκλος έχει περιφέρεια 2 πι). Αν τώρα αλλάξουμε το λόγο της κάθετης διαμέτρου με την οριζόντια (για να κάνουμε έλλειψη) το τόξο που διαγράφει για pi/6 δεν ορίζεται από τη γωνία αλλά από την περιφέρεια που τραβάει. Σε αντίθεση στο GDI+ ανεξάρτητα από το λόγο κάθετης με οριζόντιας διαμέτρου, η γωνία παραμένει ίδια. Ακόμα δεν έχω αποφασίσει αν θα το αφήσω έτσι! Διότι ήθελα αυτό που δείχνει η Μ2000 με το GDI32, δηλαδή όπως το έδειχνε από την πρώτη έκδοση, να συνεχίσει να το δείχνει με το GDI+ με τη διαφορά ότι στο δεύτερο υπάρχει ομαλοποίηση στις γραμμές και τα τόξα, για να μην φαίνονται "δοντάκια". Η ουσία είναι ότι τώρα δουλεύει αλλά ειδικά για τα τόξα δεν εμφανίζονται οι γωνίές όπως πρέπει, πρέπει να βρω έναν μαθηματικό τύπο ώστε σε κάθε περίπτωση γωνίας, στο GDI+ να παίρνω τη γωνία εκείνη που θα δίνει ότι και το GDI32. Το ζήτημα πάλι είναι "ποιό είναι το σωστό"; Μήπως πρέπει "επιτέλους" να αλλάξω το GDI32 και να παραμείνει το GDI+ ως έχει;