Δευτέρα, 29 Φεβρουαρίου 2016

Αναθεώρηση 173 και Χρήση Γεγονότων.(επέκταση)

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

(Για να τρέξει κάποιος το παράδειγμα πρέπει να έχει κατεβάσει το κώδικα της Μ2000, έχει και το m2000.exe μέσα - θέλει Unblock γιατί θα είναι κατεβασμένο από το διαδίκτυο. Μετά ξεκινάει την Μ2000 - αρχικά είναι με μπλε φόντο, άσπρα γράμματα, αλλά μπορεί κανείς να τα αλλάξει με την εντολή Ρυθμίσεις ή με Ctrl+U. Μετά γράφει Σ Α και μπαίνει στο διορθωτή για το τμήμα Α. Εκεί αντιγράφει το πρόγραμμα που είναι παρακάτω, βγαίνει με Esc και γράφει Α και Enter για να το τρέξει. Ειδικά το πλήκτρο Esc κάνει άμεση έξοδο από την εκτέλεση, ενώ το Break κάνει reset στον διερμηνευτή).

Δείτε παρακάτω τι ήθελα να κάνω και τι έκανα! Ήθελα να μεταφέρω ένα πρόγραμμα από C# σε M2000, και όχι κάτι εύκολο αλλά ένα που να χρησιμοποιεί Events γεγονότα και Delegates δηλαδή αντιπρόσωπο (το τι σημαίνει δεν μπορεί κάποιος εδώ άμεσα να το καταλάβει, θα το δούμε παρακάτω)
Για να αντιληφθεί κανείς τι παίζει αρκεί να σκεφτεί ότι έχουμε ένα αντικείμενο που χρησιμοποιεί ένα αντικείμενο Event, "το έχει δημοσιεύσει" σε όρους C#,  και σε αυτό του λέει πώς πρέπει να δηλώνεται ένα γεγονός δηλαδή τι παραμέτρους θα παίρνει (μπορεί να πάρει και με αναφορά), και αυτό το κάνει δηλώνοντας μία αντιπροσωπευτική συνάρτηση που απλά δείχνει τι παραμέτρους χρειαζόμαστε. Γιατί το κάνουμε αυτό; Γιατί θα βάλουμε σε αυτό το γεγονός κάποιες συναρτήσεις που θα καλούνται και ο τρόπος που το κάνουμε δεν δηλώνει τι παραμέτρους έχουν οι συναρτήσεις, άρα υποχρεωτικά θα πρέπει να το δηλώσουμε με κάποιο τρόπο. Η C# το κάνει με το Delegate.

Όταν ένα γεγονός συμβεί, στην ουσία, προγραμματιστικά, καλούμε στο αντικείμενο Event την εντολή Raise και περνάμε τις παραμέτρους που θέλουμε. Το αντικείμενο καλεί μια σειρά "subscribers", δηλαδή συναρτήσεις που έχουν καταχωρηθεί ως λήπτες. Τι λαμβάνουν; Λαμβάνουν μια σειρά παραμέτρων, ο κάθε λήπτης τις ίδιες παραμέτρους. Ακόμα και αυτό που περνάμε με αναφορά θα περάσει με αναφορά διαδοχικά απ΄όλους τους λήπτες! Μόλις ολοκληρωθεί η εξυπηρέτηση του γεγονότος, επιστρέφει η εκτέλεση από το σημείο που "προκλήθηκε" το γεγονός.

Στο πρόγραμμα παρακάτω δίνω επεξηγήσεις, και βασίζεται σαν ιδέα σε αυτό εδώ από το tutorials point για τα Event της C#.
Υπάρχουν αρκετές σημειώσεις. Μαθαίνουμε και άλλα πράγματα συνάμα!
Η κλάση event εδώ λέγεται TheEvent επειδή σκοπεύω να την βάλω εσωτερική στη Μ2000 και το event το κρατάω για αντικείμενο.

η παράμετρος με το σύμβολο & μπροστά είναι με πέρασμα με αναφορά. Στην ουσία αντί να περάσουμε την τιμή της μεταβλητής περνάμε σε ένα αλφαριθμητικό την "ισχνή αναφορά", δηλαδή την διεύθυνσή της. Λέγεται ισχνή αναφορά γιατί δεν έχει πραγματοποιηθεί αναφορά ακόμα. Αυτό θα γίνει όταν στο προορισμό την χρησιμοποιήσουμε για να διαβάσουμε ή να γράψουμε στην μεταβλητή. Η Μ2000 μπορεί να χρησιμοποιήσει άμεσα την αναφορά χωρίς να χρησιμοποιήσει όνομα, ή να φτιάξει ένα νέο όνομα που δείχνει εκεί που λέει η αναφορά. Ο πρώτος τρόπος είναι ο εύκολος με την έννοια ότι δεν απαιτεί νέα εγγραφή ονόματος. Ειδικά στις αναφορές συναρτήσεων αντί να περνάμε μια ισχνή αναφορά στη συνάρτηση...περνάμε το κώδικα της συνάρτησης μαζί με στοιχεία που δηλώνουν αν είναι σε αντικείμενο, ώστε οι μεταβλητές του αντικειμένου, ή άλλες συναρτήσεις και τμήματα, ακόμα και τα ιδιωτικά να είναι προσβάσιμα. Μέσα σε αλφαριθμητικό μπορούμε να έχουμε ανώνυμη συνάρτηση, και αυτό ακριβώς κάνουμε στην TheEvent, με το πίνακα αλφαριθμητικών. Η ανώνυμη συνάρτηση όμως περιέχει και κάτι που δηλώνει το περιβάλλον της. Είναι δηλαδή ένα closure με μια έννοια. Δεν είναι όλες οι ανώνυμες συναρτήσεις με δήλωση περιβάλλοντος.
Π.χ.
Τύπωσε συνάρτηση("{=αριθμός**2}",2)
θα δώσει 4
Η ανώνυμη συνάρτηση είναι το αλφαριθμητικό με τις αγκύλες. Η συνάρτηση συνάρτηση() δημιουργεί τοπικά μια συνάρτηση με αυτό το περιεχόμενο, περνάει τον αριθμό 2, την εκτελεί και γυρίζει το αποτέλεσμα.
Στη κλάση TheEvent χρησιμοποιούμε μια κανονική αναφορά, έχουμε φτιάξει την Α() και περνάμε σε αυτήν το πρότυπο των παραμέτρων "message$, &byrefValue". Η κλήση γίνεται χωρίς να πάρουμε κάποιο αποτέλεσμα (δηλώνουμε Κενή). Καλούμε δηλαδή το Α(message$, &byrefValue).

Στο παράδειγμα παρακάτω ανανέωσα την delegate ώστε το κάλεσμα της συνάρτησης να γίνεται σε νέο σωρό. Έβαλα Σημειώσεις 1 και 2 (Σημ 1: και Σημ 2 :) και οδηγίες στην delegate συνάρτηση, για να δει κανείς πως στην κλήση μπορούμε να περάσουμε ή όχι το σωρό τιμών του προηγούμενου επιπέδου!


Κλάση Γενική TheEvent {
Ιδιωτικό:
      Πίνακας cast$()
      total, param$
Δημόσιο:
      Τμήμα TheEvent {
            Αν όχι κενό τότε Διάβασε .param$
      }
      Τμήμα Add {
            .total++
            Πίνακας .cast$(.total)
            Διάβασε .cast$(.total-1)
      }
      Συνάρτηση Raise {
                  Συνάρτηση Delegate {
                        Διάβασε Νέο &Α(), p$
                        \\ για να διαβάσουμε προκαθορισμένες παραμέτρους
                        Ένθεση "Διάβασε Νέο "+p$
                         Σημ 1 : Κάλεσε κενή "Α("+p$+")"  \\ περνάμε τον σωρό στην Α()
                         \\\ ενώ εδώ περνάμε τιμές σε νέο σωρό
                         \\ βγάλτε τις σημ 1 και 2 και βάλτε μια στην επόμενη γραμμή
                         \\ θα φανει ότι η κάλεσε περνάει το σωρό
                         \\ βάλτε πάλι την σημείωση 1 και αφήστε χωρίς σημείωση το 2
                         \\ και βγάλτε τη σημείωση από εδώ για να δείτε ότι η παρακάτω γραμμή
                         \\ δεν περνάει το σωρό!
                         =συναρτηση("Α("+p$+")")
                  }
                  Αν .total=0 Τότε Έξοδος
                  z=.total-1
                  k=Μέγεθος.Σωρού
                  {
                        Αν Μέγεθος.Σωρού>k Τότε Πέτα Μέγεθος.Σωρού-k
                        Αν z>0 Τότε { Για i=1 Έως k { Πάνω k } }
                        κάλεσε κενή συνάρτηση Delegate(.cast$(z), .param$)
                        z--
                        Αν z>=0 Τότε Κυκλικά
                  }
      }
}
   \\ boiler Κλάση
Κλάση Γενική Boiler {
Ιδιωτικό:
       temp, pressure
Δημόσιο:
      Τμήμα Boiler {
             Διάβασε .temp, .pressure
      }

      Συνάρτηση getTemp {
            =.temp
      }
      Συνάρτηση getPressure {
            =.pressure
      }
}


\\  TheEvent publisher
Κλάση Γενική DelegateBoilerEvent {
      Ομάδα BoilerLogHandler
      Τμήμα DelegateBoilerEvent {
            .BoilerLogHandler<=TheEvent("message$, &byrefValue")
      }
      Τμήμα LogProcess {
               Διάβασε tempNow
               m=1000
               remarks$ = "O. K"
               b = Boiler(100, tempNow)
               t = b.getTemp()
               p = b.getPressure()
               Αν ( t > 150 ή t < 80 ή p < 12 ή p > 15) Τότε {
                  remarks$ = "Need Maintenance"
               }
               Κάλεσε .OnBoilerEventLog("Logging Info:"+ chr$(13)+chr$(10), &m)
               Κάλεσε .OnBoilerEventLog("Temparature" + str$(t) + chr$(13)+chr$(10)+"Pressure:" + str$(p), &m)
               Κάλεσε .OnBoilerEventLog(chr$(13)+chr$(10)+"Message: " + remarks$, &m)
               Τύπωσε m \\ 1006
      }
      Συνάρτηση OnBoilerEventLog {
            Κάλεσε .BoilerLogHandler.Raise()
      }
}
Κλάση Γενική BoilerInfoLogger {
Ιδιωτικό:
      filename$, fHandler, fHandlerRead
Δημόσιο:
      Τμήμα BoilerInfoLogger {
            Διάβασε .filename$
            Αν Δεν υπάρχει(.filename$) τότε {
                  Άνοιξε .filename$ Για Ευρεία Εξαγωγή Ως .fHandler
                  κλείσε #.fHandler
         }
         Άνοιξε .filename$ Για Ευρεία Συμπλήρωση Ως .fHandler
         Άνοιξε .filename$ Για Ευρεία Εισαγωγή Ως .fHandlerRead
      }
      Συνάρτηση Logger {
          Διάβασε info$, &k
          k++
           Τύπωσε # .fhandler, info$
      }
      Συνάρτηση NextLine {
            Αν όχι Τέλος(#.fHandlerRead ) Τότε {
                  Γραμμή Εισαγωγής #.fHandlerRead, Paragraph$
                  Τύπωσε Paragraph$
                  =Αληθές
            }
      }
      Τμήμα Close {
            Κλείσε #.fHandler , #.fHandlerRead
      }
   }


   \\ Η ομάδα RecordBoilerInfo δηλώνει τα γεγονότα
   \\ μέσω ενός αντικειμένου DelegateBoilerEvent()
   \\ είναι απευθείας γραμμένο σε ομάδα
Ομάδα RecordBoilerInfo {
       Συνάρτηση Logger {
                  Διάβασε Info$, &k
                  k++
                  Τύπωσε "to screen:";info$
                  Σημ 2 : Τύπωσε "stack for values size:", Μέγεθος.Σωρού \\ stack.size
            }
      Τμήμα Main {
            Διάβασε YourLogFile$
            filelog = BoilerInfoLogger(YourLogFile$)
            \\ δηλώνουμε ένα όνομα παραμέτρου ή περισσότερες
            \\ με κόμμα
            boilerEvent = DelegateBoilerEvent()
            \\-------------------------  Προσθέτουμε τους subscribers
            \\  η ακολούθους, ή συμβεβλημένους ή όπως θέλετε πείτε το!
            \\ μπωρώ να βάλω μια Για boilerEvent.BoilerLogHandler { }
            \\ αλλά πρέπει να βάλω την this ή Αυτό στην Logger() αυτού του αντικειμένου
            Για boilerEvent.BoilerLogHandler {


                  \\ το This ή Αυτό, όταν είναι αριστερά (όπως εδώ στην This.Add) ή
                  \\ όταν χρησιμοπούμε αναφορά &This ή &Αυτό τότε δείχνουμε
                  \\ στο boilerEvent.BoilerLogHandler
                  \\ Όμως στο &this.Logger() το this. ή αυτό.  που είναι δεξιά
                  \\ αναφέρται σε αυτό: RecordBoilerInfo, το αντικείμενο
                  \\ που περιέχει το κώδικα αυτό
                  This.Add &filelog.logger()
                  .Add &this.Logger()
            }
            \\ Μπορώ να χρησιμοποιήσω άμεσα αυτές τις δυο γραμμές:
            \\ Δεν χρησιμοποιώ καθόλου το This ή Αυτό
            \\ Το  &.Logger() μετριέται ως &This.Logger()
            \\ αλλά σε μια Για {} θα άλλαζε εκτός αν εμείς γράφαμε ότι είναι το This
            \\ boilerEvent.BoilerLogHandler.Add &.Logger()
            \\ boilerEvent.BoilerLogHandler.Add &filelog.logger()
            \\ -----------------------------------
            boilerEvent.LogProcess 12
            Μηνύματα()
               \\GetAllMessages()
            boilerEvent.LogProcess 11
            Μηνύματα()
            \\ GetAllMessages()
            filelog.Close
            \\Sub GetAllMessages()
            \\ για αγγλικά ονόματα χρησιμοποιούμε το Sub
            \\ για ελληνικά το Ρουτίνα ή Ρουτινα
           Ρουτίνα Μηνύματα()
                 Ενώ filelog.NextLine() { }
           Τέλος Ρουτίνας \\ ομοίως για την έξοδο
           \\End Sub
     }
}
\\ Εκκίνηση
Άδειασε  \\ αδειάζουμε το σωρό τιμών
Οθόνη  \\ καθαρίζουμε την οθόνη
RecordBoilerInfo.Main "c:\boilerNew.txt"
\\ Κλείνει όλα τα αρχεία αν έχει γίνει κάποιο λάθος
Κλείσε 0
Κονσόλα "Del c:\boilerNew.txt";











Πιο μαζεμένη και με αναλογική γραφή! Έβαλα και έναν τρόπο να αλλάζω το \n με αλλαγή γραμμών στο αρχείο και με διαστήματα στην εμφάνιση στην οθόνη!
'Εχω σχεδόν τελειώσει με το Event ως αντικείμενο της Μ2000, και δεν αργεί να βγει η αναθεώρηση 174.

\\ 4 για αναλογική γραφή
Τύπωσε $(4),
Κλάση Γενική TheEvent {
Ιδιωτικό:
      Πίνακας cast$()
      total, param$
Δημόσιο:
      Τμήμα TheEvent {
            Αν όχι κενό τότε Διάβασε .param$
      }
      Τμήμα Add {
            .total++
            Πίνακας .cast$(.total)
            Διάβασε .cast$(.total-1)
      }
      Συνάρτηση Raise {
                  Συνάρτηση Delegate {
                        Διάβασε Νέο &Α(), p$
                        Ένθεση "Διάβασε "+p$
                        =συναρτηση("Α("+p$+")")
                  }
                  Αν .total=0 Τότε Έξοδος
                  z=.total-1
                  k=Μέγεθος.Σωρού
                  {
                        Αν z>0 Τότε { Για i=1 Έως k { Πάνω k } }
                        κάλεσε κενή συνάρτηση Delegate(.cast$(z), .param$)
                        z--
                        Αν z>=0 Τότε Κυκλικά
                  }
      }
}
Κλάση Γενική Boiler {
Ιδιωτικό:
       temp, pressure
Δημόσιο:
      Τμήμα Boiler {
             Διάβασε .temp, .pressure
      }

      Συνάρτηση getTemp {
            =.temp
      }
      Συνάρτηση getPressure {
            =.pressure
      }
}
Κλάση Γενική DelegateBoilerEvent {
      Ομάδα BoilerLogHandler
      Τμήμα DelegateBoilerEvent {
            .BoilerLogHandler<=TheEvent("message$, &byrefValue")
      }
      Τμήμα LogProcess {
               Διάβασε tempNow
               m=1000
               remarks$ = "O. K"
               b = Boiler(100, tempNow)
               t = b.getTemp()
               p = b.getPressure()
               Αν ( t > 150 ή t < 80 ή p < 12 ή p > 15) Τότε {
                  remarks$ = "Need Maintenance"
               }
               Σωρός Νέος {
                     Κάλεσε .OnBoilerEventLog("Logging Info:\n", &m)
                     Κάλεσε .OnBoilerEventLog("Temparature" + str$(t) +"\nPressure:" + str$(p), &m)
                     Κάλεσε .OnBoilerEventLog("\nMessage: " + remarks$, &m)
               }
               Τύπωσε m \\ 1006
      }
      Συνάρτηση OnBoilerEventLog {
            Κάλεσε .BoilerLogHandler.Raise()
      }
}
Κλάση Γενική BoilerInfoLogger {
Ιδιωτικό:
      filename$, fHandler, fHandlerRead
Δημόσιο:
      Τμήμα BoilerInfoLogger {
            Διάβασε .filename$
            Αν Δεν υπάρχει(.filename$) τότε {
                  Άνοιξε .filename$ Για Ευρεία Εξαγωγή Ως .fHandler
                  κλείσε #.fHandler
         }
         Άνοιξε .filename$ Για Ευρεία Συμπλήρωση Ως .fHandler
         Άνοιξε .filename$ Για Ευρεία Εισαγωγή Ως .fHandlerRead
      }
      Συνάρτηση Logger {
          Διάβασε info$, &k
          k++
           Τύπωσε # .fhandler, αλλαγη$("\n",chr$(13)+chr$(10), info$);
      }
      Συνάρτηση NextLine {
            Αν όχι Τέλος(#.fHandlerRead ) Τότε {
                  Γραμμή Εισαγωγής #.fHandlerRead, Paragraph$
                  Τύπωσε Paragraph$
                  =Αληθές
            }
      }
      Τμήμα Close {
            Κλείσε #.fHandler , #.fHandlerRead
      }
   }
Ομάδα RecordBoilerInfo {
       Συνάρτηση Logger {
                  Διάβασε Info$, &k
                  k++
                  Πένα (πενα+9) υπολ 16 {
                        Τύπωσε "to screen:";Αποκ$(Αλλαγή$("\n","  ", info$))
                  }
                  Σημ 2 : Τύπωσε "stack for values size:", Μέγεθος.Σωρού \\ stack.size
            }
      Τμήμα Main {
            Διάβασε YourLogFile$
            filelog = BoilerInfoLogger(YourLogFile$)
          
            boilerEvent = DelegateBoilerEvent()
            boilerEvent.BoilerLogHandler.Add &.Logger()
            boilerEvent.BoilerLogHandler.Add &filelog.logger()
          
            boilerEvent.LogProcess 12
            GetAllMessages()
            boilerEvent.LogProcess 11
            GetAllMessages()
            filelog.Close
          
            Sub GetAllMessages()
                 Ενώ filelog.NextLine() { }
           End Sub
     }
}
Οθόνη
RecordBoilerInfo.Main "c:\boilerNew.txt"
Κονσόλα "Del c:\boilerNew.txt";




Κυριακή, 28 Φεβρουαρίου 2016

Εγχειρίδιο της Μ2000 - Τεύχος 22ο

11.4 Πολλαπλοί δρομείς, σε αρχείο κειμένου.

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

Στατική α$=έναόνομα$
Ομάδα Αλφα {
Ιδιωτικό:
      Πίνακας Α()
      νέο_α
      Τμημα άλλο {
                  Διάβασε Χ, Ζ, ΠουΑκριβώς, Αυτό$
                  Μετάθεση #Χ, .Α(Ζ-1)+(ΠουΑκριβώς-1)*2
                  Τύπωσε #Χ, Αυτό$;
      }
      Τμήμα δικόμου {
                  Διάβασε Χ, Ζ
                  Μετάθεση #Χ, .Α(Ζ-1)
                  Αν όχι κενό τότε {
                        Διάβασε Πόσα
                        Αν όχι κενό τότε {
                              Διάβασε ΠόσαΑκόμα
                              Άλλαξε Πόσα, ΠόσαΑκόμα
                              \\ η μετάθεση δουλεύει με bytes άρα επί 2
                              \\ αφού έχουμε Unicode
                              Μετάθεση #Χ, Μετάθεση(#Χ)+(ΠόσαΑκόμα-1)*2
                        }
                       Βάλε εισαγωγή$(#Χ,Πόσα)
                  } Αλλιώς {
                        Γραμμή Εισαγωγής #Χ, α$
                        Βάλε α$
                  }
            }
Δημόσιο:
      Τμήμα ΒάλεΈνα {
            Διάβασε Χ
            .νέο_α++
            Πίνακας .Α(.νέο_α)
            .Α(.νέο_α-1)=μετάθεση(#Χ)
      }
      Συνάρτηση ΓράψεΠίσω {
            .άλλο
            =0 \\ οκ
      }
      Συνάρτηση ΣτοΣημείο$ {
            \\ οι στατικές έχουν προτεραιότητα
            \\ και είναι κοινές σε κλήσεις συναρτήσεων.
            \\ οπότε τις απομονώνουμε
            .Δικόμου
            =Γράμμα$
      }      
     
}
Άνοιξε α$ για ευρεία εξαγωγή ως κ
      Αλφα.ΒάλεΈνα κ
      Τύπωσε #κ, "Μια γραμμή"
      Αλφα.ΒάλεΈνα κ
      Τύπωσε #κ, "Και άλλη μια γραμμή"
Κλείσε #κ

Αναμονή 100 \\ να είμαστε σίγουροι πως έχει γραφτεί

Άνοιξε α$ για ευρεία Εισαγωγή ως κ
      Άνοιξε α$ για ευρεία Συμπλήρωση ως κ1
            Τύπωσε κ, κ1 \\ έχουμε δυο διαφορετικούς αριθμούς χειρισμού αρχείου
            Κάλεσε Αλφα.ΓράψεΠίσω(κ1, 2, 5, "ΑΛΛΗ")
            Τύπωσε Άλφα.ΣτοΣημείο$(κ,2)
Κλείσε #κ1, #κ


11.5 Αρχεία Πεδίων (τυχαίας προσπέλασης σταθερού μήκους)

Η Μ2000 υποστηρίζει αρχεία τυχαίας προσπέλασης με έναν δρομέα, όπου διαβάζουμε ή γράφουμε μια εγγραφή με ένα μόνο πεδίο. Αυτός ο τρόπος υπάρχει για να δείξει πως παλαιότερα χρησιμοποιούνταν τα αρχεία τυχαίας προσπέλασης. Επειδή έχουμε σταθερό μήκος, δεν είναι αναγκαίο να υπάρχουν αλλαγές παραγράφων. Μάλιστα και εδώ έχουμε την επιλογή ΕΥΡΕΙΑ για unicode ή χωρίς αυτήν για ANSI.
Εδώ η εντολή Μετάθεση και η συνάρτηση Μετάθεση() δεν έχουν εφαρμογή. Κάθε εγγραφή θεωρώ πως είναι μια καρτέλα στοιχείων όπου κάθε στοιχείο έχει μια θέση και ένα πλάτος, και όλα μαζί έχουν ακριβώς το πλάτος εγγραφής. Μπορώ να ξέρω πόσες εγγραφές έχω με τη συνάρτηση Εγγραφές(). 

α$="βασικό_αρχείο1"
Συνάρτηση Υπενθύμιση$ {
      Διάβασε κκ, χχ
      Πάρε #κκ, έναπεδίο$, χχ
      =Μεσ$(έναπεδίο$,31)
      Αν όχι κενό τότε {
         \\ η παρεμβολή για αλφαριθμητικό
         \\ κάνει καταχώρηση αν δώσουμε δεύτερη παράμετρο
         \\ που λέει πόσοι χαρακτήρες θα διαγραφούν
         Παρεμβολή 31,70 έναπεδίο$=πεδίο$(γράμμα$, 70)
         Δώσε #κκ, έναπεδίο$, χχ
      }
}
Άνοιξε α$ για ευρεία πεδία ως κ μήκος=200 \\ μήκος σε bytes άρα 200/2
      Τύπωσε Εγγραφές(κ) \\ 0 την πρώτη φορά και 2 την δεύτερη
      \\ Κάθε εγγραφή είναι μια "καρτέλα", με μικρά πεδία, που γράφονται σε ένα πεδίο!
      Αν Εγγραφές(κ)=0 Τότε {
            Δώσε #κ, πεδίο$("Γιώργος Κ",20)+"6934565612"+Πεδίο$("Οκ",70), 1
            Δώσε #κ, πεδίο$("Πέτρος ",20)+"6963845656"+Πεδίο$("Οφείλει 1000 ευρώ",70), 2
      }
      ΔείξεΕνα(κ,1)
      ΔείξεΕνα(κ,2)
      \\ αλλάζω πεδίο και ξαναδιαβάζω την "καρτέλα"
      παλιό$=Υπενθύμιση$(κ, 2, "Πλήρωσε οκ")
      ΔείξεΕνα(κ,2)
κλείσε #κ

Ρουτίνα ΔείξεΕνα(κκ, χχ)
      Τοπική έναπεδίο$
      Πάρε #κκ, έναπεδίο$, χχ
      Τύπωσε "Όνομα:",Αρισ$(έναπεδίο$,20)
      Τύπωσε "Τηλ:", Μεσ$(έναπεδίο$,21,10)
      Τύπωσε "Υπενθύμηση:"
      Τύπωσε @(5), Μεσ$(έναπεδίο$,31)
Τέλος Ρουτίνας

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

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


Για να βάλω αριθμό σε πεδίο έχω μερικούς τρόπους:
α$=μορφή$("[{0:3:10}]",12.12121)
Τύπωσε α$
α$=μορφή$("[{0:3:-20}]",1224.12121)
Τύπωσε α$
α$=μορφή$("{0}[{1:3:10}]","χ=",12.12121)
Τύπωσε α$
α$=μορφή$("{0}[{1:3:-20}]","ζ=",1224.12121)
Τύπωσε α$

Η συνάρτηση μορφή$() επιστρέφει ένα αλφαριθμητικό. Δέχεται τουλάχιστον ένα αλφαριθμητικό. Μπορεί να δεχτεί στην συνέχεια μια λίστα τιμών ως μια σειρά όπου κάθε τιμή θα είναι  αλφαριθμητικό ή αριθμός (μπορούμε να δώσουμε παραστάσεις π.χ. 12+3), Στο πρώτο αλφαριθμητικό (και αυτό μπορεί να είναι αλφαριθμητική παράσταση) έχουμε την δυνατότητα να δώσουμε στοιχεία για το που θα μπουν τα στοιχεία της λίστας. Το πρώτο στοιχείο έχει τον αριθμό 0, έτσι ένα {0} σημαίνει βάλε την τιμή του πρώτου στοιχείου. Μπορούμε να το βάλουμε σε περισσότερες από μία  θέσεις, και μάλιστα μπορεί στο ενδιάμεσο να βάλουμε το {1} ώστε να μπει η δεύτερη τιμή. Βλέπουμε στο παράδειγμα ότι υπάρχει ο χαρακτήρας ":" και μάλιστα δυο φορές δίπλα από τον αριθμό στοιχείου (όταν σε μια τιμή ορίζεται διακριτή θέση, με κάποιο νόημα, τότε λέγεται στοιχείο, π.χ. ένας αριθμός 5 στο σκορ ενός παιχνιδιού δεν είναι απλά μια τιμή 5 αλλά το σκορ με τιμή 5 άρα είναι στοιχείο, τιμή με νόημα). Πράγματι αυτό συμβαίνει γιατί έχουμε δυο  δυνατές παραμέτρους, τον αριθμό δεκαδικών (στρογγυλοποίησης, αλλά συμπληρώνει μηδενικά αν χρειαστεί), και το εύρος του πεδίου (αυτό μας ενδιαφέρει στο θέμα μας). Το εύρος πεδίου δίνεται σε χαρακτήρες και αν είναι αρνητικό σημαίνει δεξιά στοίχιση, ενώ θετικό αριστερή.

11.6 Αρχεία XML με χρήση Msxml2.DOM

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

“Δεδομένα είναι τα στοιχεία που δίνονται σε μια διεργασία, και τα στοιχεία είναι τιμές με νόημα. Η διεργασία τροφοδοτεί τα δεδομένα ως τιμές σε παραμέτρους συναρτήσεων, και οι παράμετροι δίνουν τιμές μέσω αριθμητικών ή άλλων παραστάσεων σε μεταβλητές/πίνακες που συγκεντρώνουν την πληροφορία. Η πληροφορία είναι δηλαδή το αποτέλεσμα επεξεργασίας δεδομένων, και ακριβώς αυτό είναι το αντικείμενο της πληροφορικής. Να γιατί το αρχείο, και ο τρόπος χειρισμού έχει μεγάλη σημασία στη πληροφορική. Tα λειτουργικά συστήματα λέγονταν DOS disk operating system γιατί κύρια δουλειά τους ήταν να υποστηρίζουν αρχεία σε δίσκο, σε αποθηκευτικό μέσο”

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

Στη Μ2000 θα χρησιμοποιήσουμε ένα αντικείμενο που δίνει η Microsoft. Εκτός από αντικείμενα που σχηματίζουν οι ομάδες, έχουμε και έναν δεύτερο τύπο αντικειμένων, που δεν μπαίνουν σε ομάδες. (δεν μπορούμε να αντιγράψουμε τέτοιο αντικείμενο, αλλά μπορούμε να το περάσουμε με αναφορά). Θα χρησιμοποιήσουμε ένα αντικείμενο COM το οποίο δεν είναι γραμμένο σε M2000, δεν αποτελεί δε κάποια εξωτερική βιβλιοθήκη ρουτινών αλλά όταν το συνδέουμε στην εφαρμογή (λέγεται καθυστερημένη σύνδεση, διότι δεν έχει γίνει με την εκκίνηση της εφαρμογής αλλά μετά από παραγγελία)
από την έκδοση 8.9 αναθεώρηση 2 μπορούμε να φορτώνουμε χωρίς το BOM και με το δείκτη -2 λέμε να σώνει κείμενο σε UTF-8 χωρίς BOM. Στην ανάγνωση το βρίσκει η Φόρτωσε.Έγγραφο και το βάζει στο έγγραφο, ώστε στο σώσιμο να διατηρήσει την έλλειψη του BOM (κάποια bytes, που δηλώνουν στην αρχή του κειμένου το τύπο αρχείο κειμένου).

Προστέθηκε στο παράδειγμα και ο τρόπος που διατρέχουμε όμοια στοιχεία και διαβάζουμε ένα στοιχείο από αυτά. Χρησιμοποιήθηκαν ονόματα μεταβλητών από το παράδειγμα εδώ:
https://msdn.microsoft.com/en-us/library/ms757073(v=vs.85).aspx

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


Έγγραφο α$={<?xml version="1.0"?>
<Ονόματα>
      <στοιχείο αα="69">
      <όνομακωδ>VB6</όνομακωδ>
      <ποσότητα>2</ποσότητα>
      </στοιχείο>
      <στοιχείο αα="70">
      <όνομακωδ>C++</όνομακωδ>
      <ποσότητα>3</ποσότητα>
      </στοιχείο>
</Ονόματα>
}
Σώσε.Έγγραφο α$,"άλφα.xml", -2
Αναμονή 100
Όρισε ΑντικείμενοXml "Msxml2.DOMDocument.6.0"
Μέθοδος ΑντικείμενοXml, "Load", κατ$+"άλφα.xml" ως οκ
Αν οκ τότε {
      Όρισε ΣτοιχείοXML με ΑντικείμενοXml, "documentElement"
      Μέθοδος ΣτοιχείοXML, "selectSingleNode", "στοιχείο[@αα=70]" ως ΕπιλεγμένοΣτοιχείο
      Μέθοδος ΕπιλεγμένοΣτοιχείο, "selectSingleNode", "όνομακωδ" ως ΕπιλεγμένοΠεδίο
      \\ αυτή η Με είναι διαφορετική από την Με στην δομή Επίλεξε Με
      \\ εδώ έχουμε εντολή ενώ εκεί είναι στοιχείο της δομής
      Με ΕπιλεγμένοΠεδίο, "text" ως η_τιμή_μου$
      Τύπωσε η_τιμή_μου$
      \\ Μπορώ να αλλάξω τιμή
      η_τιμή_μου$="George"
      \\ Και μπορώ να σώσω
      Μέθοδος ΑντικείμενοXml, "Save", κατ$+"alfa.xml" ως ok
     Μέθοδος ΣτοιχείοXML, "getElementsByTagName", "στοιχείο" ως objNodeList
     Με objNodeList , "length" ως objNodeList.length
     Τύπωσε objNodeList.length
     Για ι=0 έως objNodeList.length-1 {
           Μέθοδος objNodeList , "item", ι ως objNodeList.item
            Μέθοδος objNodeList.item, "getElementsByTagName", "όνομακωδ" ως objNodeList2
            Μέθοδος objNodeList2 , "item", 0 ως objNodeList.item2
           'Με  objNodeList.item2 , "xml" ως α12$
           Με objNodeList.item2 , "text" ως α12$
           Pen 12 {
                 Τύπωσε α12$
           }
           Όρισε objNodeList.item2 Τίποτα
           Όρισε objNodeList2 Τίποτα
           Όρισε objNodeList.item Τίποτα
     }
      Όρισε objNodeList Τίποτα
      Όρισε ΕπιλεγμένοΠεδίο Τίποτα
      Όρισε ΕπιλεγμένοΣτοιχείο Τίποτα
      Όρισε ΣτοιχείοXML Τίποτα
}
Όρισε ΑντικείμενοXml Τίποτα
Εγγραφο ααα$
Φόρτωσε.Έγγραφο ααα$, "alfa.xml"
Αναφορά ααα$



Επεξήγηση (απλοποιημένη όσο γίνεται):
Τα αντικείμενα τύπου COM Component Object Model είναι μια τεχνολογία που δίνει αντικείμενα σε εκτελέσιμη μορφή για να χρησιμοποιηθούν από άλλες διεργασίες, όπως εδώ από τη Μ2000, ενώ υπάρχει σύστημα αδειών, ώστε τα αντικείμενα να μπορούν να χρησιμοποιηθούν μόνο από αυτή την εφαρμογή που έχει άδεια να το κάνει. Με αυτό τον τρόπο γράφτηκε κλειστός κώδικας, ιδιοταγής/ιδιόκτητος, με σκοπό το κέρδος, δηλαδή να πουλιέται σε όποιον τον έχει ανάγκη. Το λειτουργικό σύστημα Windows ενσωματώνει αυτή την τεχνολογία (έρχεται από το 1994) και μάλιστα την επεκτείνει, αλλά εδώ δεν θα μας απασχολήσει αυτό.

Αυτό που έχει σημασία για το εγχειρίδιο είναι ότι τα αντικείμενα ορίζονται σε ένα όνομα βάσει μιας ταυτότητας-περιγραφής: "Msxml2.DOMDocument.6.0", και όταν δεν τα χρειαζόμαστε πρέπει να ορίσουμε στο όνομα που συνδέεται με το αντικείμενο να πάρει το Τίποτα. Και να το ξεχάσουμε η Μ2000 θα το κάνει για μας, αλλά είναι καλή πρακτική να το κάνουμε με μια δήλωσε Όρισε όνομακάτι Τίποτα

Ορίζουμε αντικείμενα ως αποτέλεσμα μεθόδων (εδώ συνάρτηση/μέθοδος είναι ένα πράγμα), Τα ονόματα των μεθόδων είναι στα αγγλικά. Για παράδειγμα η μέθοδος "selectSingleNode" αναφέρεται εδώ  στο XML DOM METHODS. Βλέπουμε ότι έχουν βάλει παραδείγματα σε jscript και C/C++.  Ο σκοπός των αντικειμένων COM (ή DOM ως σχετική ορολογία) είναι να μπορούμε να ενώσουμε κώδικα με αντικείμενο έτοιμο για εκτέλεση.

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

Ορισε ΠροσθεσεΠλαγιαΓραμμη Απο "Shlwapi.PathAddBackslashW" { &Μονοπατι$ }
ένα_μονοπάτι$ = "C:"+Επαν$(Χαρ$(0), 250)
Κάλεσε Κενή ΠροσθεσεΠλαγιαΓραμμη(&ένα_μονοπάτι$)
Σημ 1 : Α=ΠροσθεσεΠλαγιαΓραμμη(&ένα_μονοπάτι$)
Τύπωσε ΑριστερόΜέρος$(ένα_μονοπάτι$,Χαρ$(0))



Στο κύριο πρόγραμμα (για το xml) συνδέουμε μια μεταβλητή με την ιδιότητα ενός αντικειμένου:
Με ΕπιλεγμένοΠεδίο, "text" ως η_τιμή_μου$
Αν δώσουμε την παρακάτω εντολή για να δούμε τους τύπους των μεταβλητών:
Τύπωσε τύπος$(η_τιμή_μου$), τύπος$(ΕπιλεγμένοΠεδίο)
PropReference  IXMLDOMElement
Θα πάρουμε τα ονόματα των αντικειμένων. Το η_τιμή_μου$ δεν είναι απλά μια αλφαριθμητική μεταβλητή αλλά ένα αντικείμενο που δίνει τιμή αλφαριθμητική και δέχεται επίσης τιμή αλφαριθμητική, την οποία προωθεί στο αντικείμενο τύπου IXMLDOMElement που λέμε εδώ ΕπιλεγμένοΠεδίο και ειδικότερα στην ιδιότητα text.

Πρέπει να κάνουμε μια νέα σειρά ορισμών (για τα ίδια ονόματα) βάζοντας άλλες παραμέτρους στη κλήση της πρώτης μεθόδου, ώστε να πάρουμε το στοιχείο με αα=69 χωρίς να χρειαστεί άλλη φορά να συνδέσουμε την η_τιμή_μου$ με το ιδιότητα text (και να το κάνουμε πάλι δεν υπάρχει πρόβλημα). Οι αναφορές σε αυτά τα αντικείμενα μπορούν να αλλαχτούν, και κάποιο αντικείμενο μείνει χωρίς αναφορά (τις μετράει εσωτερικά) τότε διαγράφεται αυτόματα (να γιατί δηλώνουμε Τίποτα, όταν θέλουμε να σβήσουμε ένα αντικείμενο). Οι εντολές Όρισε,  Μέθοδος και Με δεν δέχονται στατικές μεταβλητές.

      Μέθοδος ΣτοιχείοXML, "selectSingleNode", "στοιχείο[@αα=69]" ως ΕπιλεγμένοΣτοιχείο
      Μέθοδος ΕπιλεγμένοΣτοιχείο, "selectSingleNode", "όνομακωδ" ως ΕπιλεγμένοΠεδίο
      Τύπωσε η_τιμή_μου$
      


Μια τελευταία επισήμανση. Δείτε στο πρόγραμμα ότι φορτώνουμε το xml αρχείο, το χειριζόμαστε, σβήνοντας ή αλλάζοντας κάποια στοιχεία, όμως οι αλλαγές δεν έχουν γίνει στο αρχείο. Για να γίνουν πρέπει να το σώσουμε. Αυτό σημαίνει ότι το αρχείο αυτό δεν μπορεί να λειτουργήσει μέσω του αντικειμένου ταυτόχρονα για πολλά προγράμματα. Για εργασίες όπου τα αρχεία μπορούν να αλλαχθούν ταυτόχρονα από πολλούς χρήστες έχουμε τις βάσεις δεδομένων. Τις οποίες θα δούμε σε άλλο τεύχος (Η Μ2000 χειρίζεται βάσεις δεδομένων και SQL ερωτήματα).