Τρίτη 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




Ένας άλλος τρόπος για να κάνουμε μια μια συνδεδεμέμη λίστα είναι να χρησιμοποιήσουμε Διάρθρωση μνήμης (Buffer) βάσει μιας Δομής (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, δηλαδή να χρησιμοποιήσει ένα αντικείμενο, που πριν την εκκίνηση του προγράμματος δεν του ήταν γνωστό (αυτό συμβαίνει με την γλώσσα, όπου ορισμένα αντικείμενα όπως οι φόρμες, ενώ είναι εσωτερικά στο κώδικα του περιβάλλοντος, πρoσeγγίζονται από την γλώσσα Μ2000 ως αντικείμενα εξωτερικά, και για το λόγο αυτό τα ονόματα των μεθόδων και των ιδιοτήτων δίνονται ως αλφαριθμητικά στα αγγλικά).
Αυτό που ψάχνω τελευταία είναι ο τρόπος χρήσης του IEnumVariant για να προσεγγίζω λίστες (collections) που επιστρέφουν ορισμένα εξωτερικά αντικείμενα (και δεν έχουν σχέση με τα collections της Visual Basic 6, εκτός από ένα κοινό, τη διεπαφή, η οποία είναι ίδια)











Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.