Προγράμματα σε ΓΛΩΣΣΑ της ΑΕΠΠ (3ης Λυκείου) Σελίδα 4

Μέρος 4ο (προηγούμενο)
Τα προγράμματα σε αυτή τη σελίδα αναφέρονται σε δημοσιεύσεις που έχουν γίνει στο http://alkisg.mysch.gr/steki/index.php αλλά η διαχειριστική ομάδα του συγκεκριμένου τόπου θέλει να τα αφαιρέσει, με τι δικαιολογία ότι "αποσπούν" την προσοχή των μαθητών και των καθηγητών από τα προβλεπόμενα. Σέβομαι την άποψη του διαχειριστή που επικοινώνησε μαζί μου, και για "ιστορικούς λόγους" θα μπουν τα προγράμματα εδώ:

τα προγράμματα βρίσκονται εδώ σε αρχείο zip.


Δύσκολη Άσκηση από τον Bugman

Πρόγραμμα ΑΛΦΑ.glo

Δίνεται ένας πίνακας Ν μεγέθους, μεγαλύτερος ή ίσος με 1. Δίνεται ένας αριθμός Μ ομάδων μεγαλύτερος ή ίσος με 1. Για κάθε ομάδα δίνεται ένας αριθμός μήκους. Σκοπός μας είναι να γεμίσουμε τον πίνακα με όλες τις ομάδες, και να το κάνουμε αυτό για όλους τους συνδυασμούς, σύμφωνα με τις παρακάτω οδηγίες. Μια ομάδα με μήκος 2 πιάνει δυο συνεχόμενες θέσεις στο πίνακα, και ως τιμή βάζουμε το 1 σε αυτές τις θέσεις. Οι ομάδες μπαίνουν με τη σειρά που δόθηκαν, και μεταξύ τους υποχρεωτικά θα έχουν μια κενή θέση. Οι κενές θέσεις στο πίνακα είναι με τιμή 0.
Αν δοθεί πίνακας Ν=5 και Μ=2 με Μ(1)=2 και Μ(2)=1 τότε θα πρέπει το πρόγραμμα να δώσει τις παρακάτω σειρές:
1 1 0 1 0
1 1 0 0 1
0 1 1 0 1

Δείξτε με τον κατάλληλο αλγόριθμο το αποτέλεσμα για
Ν=5 Μ=1 Μ(1)=1
Ν=10 Μ=1 Μ(1)=8
Ν=15 Μ=4 Μ(1)=3 Μ(2)=2 Μ(3)=3 Μ(4)=2

Δείξτε με τον ίδιο αλγόριθμο ότι το παρακάτω δεν είναι εφικτό
Ν=5 Μ=2 Μ(1)=2 Μ(2)=3

Για την λύση:
Η λύση δεν κάνει αναδρομή, και αντί αυτού χρησιμοποιεί  μια μεταβλητή με όνομα ΣΤΟΙΒΑ και δυο πίνακες ΣΠ[] και ΣΚ[] όπου βάζει το Π το τρέχον στοιχείο του ΠΙΝ[] και το Κ το επιλεγμένο στοιχείο από το πίνακα ΜΠΛΟΚ[].
Για ευκολία έχουμε έναν πίνακα ΜΗΚΟΣ[] όπου για το 1 δίνει όλο το μήκος μέχρι το τέλος που πρέπει να έχει ελάχιστο ο πίνακας για να χωρέσει η σειρά. Για κάθε Ι μέχρι το Μ έχουμε το μήκος από το Ι μπλοκ μέχρι το τέλος. Δείτε ότι το τελευταίο μπλοκ το Μ δεν έχει ανάγκη το ένα κενό (ως διαχωριστικό) και έτσι μπαίνει με το μήκος που δόθηκε στο ΜΠΛΟΚ(Μ).

Προστέθηκε η περίπτωση του Μ να είναι 0 (να έχουμε κενά κελιά), το οποίο δεν το ζητάει η άσκηση αλλά μπήκε εδώ!


Έξοδος
ΔΩΣΕ ΑΡΙΘΜΟ ΚΕΛΙΩΝ:
5
ΔΩΣΕ ΑΡΙΘΜΟ ΜΠΛΟΚ
1
ΜΕΓΕΘΟΣ ΜΠΛΟΚ:1
1
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1

ΔΩΣΕ ΑΡΙΘΜΟ ΚΕΛΙΩΝ:
10
ΔΩΣΕ ΑΡΙΘΜΟ ΜΠΛΟΚ
1
ΜΕΓΕΘΟΣ ΜΠΛΟΚ:1
8
1 1 1 1 1 1 1 1 0 0
0 1 1 1 1 1 1 1 1 0
0 0 1 1 1 1 1 1 1 1

ΔΩΣΕ ΑΡΙΘΜΟ ΚΕΛΙΩΝ:
15
ΔΩΣΕ ΑΡΙΘΜΟ ΜΠΛΟΚ
4
ΜΕΓΕΘΟΣ ΜΠΛΟΚ:1
3
ΜΕΓΕΘΟΣ ΜΠΛΟΚ:2
2
ΜΕΓΕΘΟΣ ΜΠΛΟΚ:3
3
ΜΕΓΕΘΟΣ ΜΠΛΟΚ:4
2
1 1 1 0 1 1 0 1 1 1 0 1 1 0 0
1 1 1 0 1 1 0 1 1 1 0 0 1 1 0
1 1 1 0 1 1 0 1 1 1 0 0 0 1 1
1 1 1 0 1 1 0 0 1 1 1 0 1 1 0
1 1 1 0 1 1 0 0 1 1 1 0 0 1 1
1 1 1 0 1 1 0 0 0 1 1 1 0 1 1
1 1 1 0 0 1 1 0 1 1 1 0 1 1 0
1 1 1 0 0 1 1 0 1 1 1 0 0 1 1
1 1 1 0 0 1 1 0 0 1 1 1 0 1 1
1 1 1 0 0 0 1 1 0 1 1 1 0 1 1
0 1 1 1 0 1 1 0 1 1 1 0 1 1 0
0 1 1 1 0 1 1 0 1 1 1 0 0 1 1
0 1 1 1 0 1 1 0 0 1 1 1 0 1 1
0 1 1 1 0 0 1 1 0 1 1 1 0 1 1
0 0 1 1 1 0 1 1 0 1 1 1 0 1 1

ΔΩΣΕ ΑΡΙΘΜΟ ΚΕΛΙΩΝ:
5
ΔΩΣΕ ΑΡΙΘΜΟ ΜΠΛΟΚ
2
ΜΕΓΕΘΟΣ ΜΠΛΟΚ:1
2
ΜΕΓΕΘΟΣ ΜΠΛΟΚ:2
3
ΑΔΥΝΑΤΟΝ

ΔΩΣΕ ΑΡΙΘΜΟ ΚΕΛΙΩΝ:
5
ΔΩΣΕ ΑΡΙΘΜΟ ΜΠΛΟΚ
0
0 0 0 0 0

Κωδικοποίηση Μηνύματος.

Πρόγραμμα ΚΩΔΙΚΑΣ_αλφα.glo
Αυτό το πρόγραμμα εξάγει ένα πρόγραμμα. Ορισμένα προγράμματα φτιάχνονται για αυτοματοποίηση. Μια έννοια της αυτοματοποίησης είναι να ετοιμάζουμε αυτόματα ένα πρόγραμμα!

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


Σύγκριση προγράμματος σε ΓΛΩΣΣΑ με αντίστοιχο σε C#


Πρόγραμμα BookNoOOP.glo
και testCsharp.zip

Παρακάτω είναι δυο προγράμματα, το ελληνικό σε ΓΛΩΣΣΑ και το αγγλικό σε C#. Το πρόγραμμα σε ΓΛΩΣΣΑ φτιάχτηκε με διαδικασίες, και με το βασικό κορμό με πίνακες, όπου κάθε πίνακας είναι και ένα πεδίο μας δομής δεδομένων Βιβλίο. Στη ΓΛΩΣΣΑ υποχρεωτικά ορίζουμε το μέγεθος των πινάκων. Έτσι μπορούμε να έχουμε 100 το πολύ βιβλία. και ο αριθμός τους είναι στην μεταβλητή ΜΕΤΡ.

Η C# δεν έχει διαδικασίες ή συναρτήσεις, αλλά Λειτουργίες, είτε γυρίζουν τιμές είτε απλά καλούνται. Όμως πρέπει να καθοριστούν οι τυπικές παράμετροι, και ο τύπος της επιστροφής, αν έχουμε επιστροφή. Οι λειτουργίες ανήκουν σε αντικείμενα. Τα αντικείμενα έχουν δυο βασικούς τύπους, είναι είτε Δομές (Structs) είτε Κανονικά (Class). Η διαφορά τους είναι ότι στην εκχώρηση η δομή δίνει αντίγραφο, ενώ το κανονικό αντικείμενο δίνει τον δείκτη του. Έτσι με εκχώρηση Α=Β στις δομές έχουμε δυο δομές με ίσο περιεχόμενο, ενώ με εκχώρηση Γ=Δ κανονικών αντικειμένων έχουμε δυο μεταβλητές που δείχνουν στο ίδιο αντικείμενο.  Για να "καθαρίσει" η μνήμη ενός αντικειμένου δεν πρέπει να υπάρχει αναφορά σε αυτό, δηλαδή τα Γ και Δ να έχουν διαγραφεί. Στις δομές δεν έχουμε δείκτη, έτσι η διαγραφή γίνεται με τη διαγραφή αυτού που ανήκει.

Στη C# δίνουμε σε ορισμό την δομή τύπου Struct με όνομα Book , η οποία έχει τέσσερα πεδία (γράφει δηλαδή μια γραμμή, ένα βιβλίο), καθώς και μια λειτουργία Βοοκ() η οποία παίρνει τέσσερις τιμές και τις γράφει στα αντίστοιχα πεδία (αυτή που έχει ίδιο όνομα με το αντικείμενο, λέγεται κατασκευαστής). Για να πάρουμε πράγματι μια νέα γραμμή, πρέπει να καλέσουμε με το new την Book() και θα μας επιστρέψει ένα αντικείμενο ως μια γραμμή εγγραφών μιας βάσης δεδομένων  (σαν ένα οριζόντιο πίνακα, αλλά αντί για δείκτη πρέπει να δίνουμε το όνομα του πεδίου στη μεταβλητή που περιέχει το Struct)

Επειδή θέλουμε πίνακα με βιβλία, στη C# πρέπει να ορίσουμε ένα αντικείμενο που θα παίρνει βιβλία, το BookDB δηλαδή Book Data Base, δηλαδή τα τέσσερα πεδία για κάθε βιβλίο. Αφού περιγράψουμε το αντικείμενο class BookDB μετά πρέπει να δημιουργήσουμε ένα με την new, και πράγματι:

BookDB bookDB = new BookDB();


(στη C# το bookDB με το BookDB είναι διαφορετικά αναγνωριστικά - η διαφορά κεφαλαία με πεζά είναι σημαντική. Επίσης χρησιμοποιούμε αρχικό γράμμα κεφαλαίο για ορισμούς και λειτουργίες, και πεζό για ό,τι άλλο όπως τις μεταβλητές)

Λέμε ότι η bookDB είναι τύπου BookDB και εκχωρούμε ένα νέο BookDB()  (το () σημαίνει ότι δεν παρέχουμε καμία τιμή στον κατασκευαστή της, όπου εδώ δεν υπάρχει - δεν υπάρχει λειτουργία BookDB
Αν δούμε τον ορισμό της bookDB θα δούμε τρία πράγματα, τη list που ορίζουμε ως αντικείμενο ArrayList() (με κενά στοιχεία στην αρχή) και δυο λειτουργίες την AddBook και την ProcessPaperbackBooks. Η list δεν λέει Public στην αρχή, είναι ιδιωτική. Ας δούμε την πρώτη λειτουργία την AddBook.

      public void AddBook(string title, string author, decimal price, bool paperBack)
      {
         list.Add(new Book(title, author, price, paperBack));
      }



Πρώτα λέμε μερικά πράγματα για την λειτουργία αυτή, όπως ότι θα είναι δημόσια, δηλαδή αν Α είναι ο δείκτης σε ένα αντικείμενο τύπου BookDB, τότε το να "καλέσουμε" την Α.AddBook θα είναι εντάξει (αφού το Α έχει πάρει αντικείμενο BookDB). Επίσης λέμε void δηλαδή κενής επιστροφής, δεν θα έχουμε επιστροφή (είναι Διαδικασία).

Οι τυπικές παράμετροι είναι δυο τύπου ΧΑΡΑΚΤΗΡΕΣ, ένας τύπου decimal (αριθμός κινητής υποδιαστολής με καθορισμένα ψηφία, χωρίς εκθέτη, εσωτερικά ο αριθμός είναι ακέραια τιμή, και απλά μεταφέρεται η υποδιαστολή για τη σωστή αναπαράσταση), καθώς και μια λογική. Αυτή η πρώτη γραμμή λέγεται και "υπογραφή". Εντός της λειτουργίας (ανάμεσα στις αγκύλες, μετά την "υπογραφή" της) χρησιμοποιούμε τη λειτουργία Add() της list. Αυτός ο τύπος Arraylist παίρνει οτιδήποτε και εδώ δίνουμε εντός των παρενθέσεων της Add() το new Book(με τέσσερις πραγματικές παραμέτρους). Δεν μας ενδιαφέρει εδώ το ΜΕΤΡ που θέλαμε για την έκδοση της ΓΛΩΣΣΑ. Σίγουρα με κάθε νέα προσθήκη, εσωτερικά το Arraylist έχει τρόπο να βρίσκει που τελειώνουν τα ήδη καταχωρημένα αντικείμενα και να προσθέτει ανάλογα.

Το ενδιαφέρον, σε αυτό το μήνυμα είναι το τρίτο μέλος της BookDB. Και αυτή είναι δημόσια και κενή (είναι διαδικασία), και δέχεται μια παράμετρο, την processBook, του τύπου ProcessBookDelegate.

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

Αυτό το b θα περαστεί ως παράμετρος στην processBook(). Έτσι τώρα καταλαβαίνουμε ότι η processBook τυπική παράμετρος παίρνει μια λειτουργία (δεν υπάρχει ανάλογο στη ΓΛΩΣΣΑ)


      public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
      {
         foreach (Book b in list)
         {
            if (b.Paperback)
            // Calling the delegate:
               processBook(b);
         }
      }



Γιατί γίνεται το παραπάνω; Η list είναι ιδιωτική. Αν δεν ήταν τότε το foreach θα το είχαμε έξω από το αντικείμενο και θα λέγαμε b in bookDB.list, αλλά τώρα αυτό δεν γίνεται, η list δεν φαίνεται έξω από το αντικείμενο (το bookDB.ΧΧΧ δηλώνει ότι το ΧΧΧ είναι δημόσιο).

Έτσι η ProcessPaperbackBooks παίρνει μια εξωτερική λειτουργία, και την καλεί για κάθε βιβλίο παρέχοντας το βιβλίο (τύπου Book) ως παράμετρο.

Θέλουμε να χρησιμοποιούμε την ProcessPaperbackBooks με διαφορετικές εξωτερικές λειτουργίες. Στη C# πρέπει να καθορίζουμε την υπογραφή της λειτουργίας πριν την εκτέλεση. Κατά την εκτέλεση η οποιαδήποτε λειτουργία δοθεί στην ProcessPaperbackBooks θα έχει την ίδια υπογραφή (Εδώ θέλουμε μια τυπική παράμετρο τύπου Book).

Για το σκοπό αυτό έχουμε δώσει στη C# αυτό:

public delegate void ProcessBookDelegate(Book book);


Το οποίο ορίζει τι είναι το ProcessBookDelegate, μια λειτουργία χωρίς επιστροφή τιμής και λαμβάνει μια παράμετρο τύπου Book.


Τι κάνουμε στη ΓΛΩΣΣΑ;

Φτιάχνουμε τις 100 θέσεις για βιβλία, καλούμε τη διαδικασία ΝΕΟ_ΒΙΒΛΙΟ() για τέσσερα βιβλία, και σε κάθε κλήση αλλάζει ο ΜΕΤΡ (μετρητής που δείχνει το τελευταίο βιβλίο, με αρχική τιμή το 0, στην ουσία είναι και συνολικός αριθμός βιβλίων).

Για να διαβάσουμε τα ΜΕΤΡ βιβλία αρκεί μια Ι από 1 μέχρι ΜΕΤΡ για να τα διαβάσουμε από τους πίνακες. Οι διαδικασίες πρέπει να πάρουν ότι χρειαστούν, και υπάρχουν έξω από αυτές, ως παράμετρο. Στο πρόγραμμα της C# αρκεί να δώσουμε για το αντικείμενο, μια εξωτερική λειτουργία. Όλα τα άλλα είναι ήδη στημένα τόσο μέσα στο αντικείμενο όσο και στην εξωτερική λειτουργία. Εύκολα θα μπορούσαμε να είχαμε πολλά αντικείμενα BookDB (με άλλες συλλογές βιβλίων) και αρκετές εξωτερικές λειτουργίες, και να δίνουμε όποια θέλουμε σε όποιο αντικείμενο θέλουμε.

Οι αντίστοιχες εξωτερικές λειτουργίες της C# στη ΓΛΩΣΣΑ είναι οι διαδικασίες:

ΛΙΣΤΑ_ΒΙΒΛΙΩΝ_ΤΣΕΠΗΣ
ΒΙΒΛΙΑ_ΜΕΣΗ_ΤΙΜΗ_ΜΕ_ΒΙΒΛΙΟ_ΤΣΕΠΗΣ

Σε κάθε περίπτωση έχουμε μια ΓΙΑ Ι ΑΠΟ 1 ΜΕΧΡΙ ΜΕΤΡ και επειδή μας ενδιαφέρουν τα βιβλία τσέπης ελέγχουμε τη τύπου ΛΟΓΙΚΗ τη ΒΙΒΛΙΟ_ΤΣΕΠΗΣ[Ι].
Η δεύτερη διαδικασία πρέπει να κρατάει τη σούμα των τιμών και να μετράει πόσα είναι τα βιβλία τσέπης, στην μεταβλητή Τόσα, για να βγάλει τη μέση τιμή ΣΟΥΜΑ/ΤΟΣΑ. Στη ΓΛΩΣΣΑ είναι εύκολο γιατί έχουμε τις εντολές εντός της επανάληψης.

Στη C# όμως δεν είναι εύκολο! Επειδή η ProcessPaperbackBooks έχει καθορισμένο κώδικα, και για κάθε βιβλίο εκτελεί τη λειτουργία processBook() (την οποία δεν γνωρίζει, παρά μόνο ότι θα πάρει ένα βιβλίο ως παράμετρο), τα Σούμα και Τόσα, πρέπει να διατηρούνται σε κάθε νέα κλήση. Αυτό σημαίνει ότι η λειτουργία πρέπει να ανήκει σε ένα αντικείμενο, το οποίο κρατάει "κατάσταση". Αυτή είναι η χρησιμότητα των αντικειμένων.
Ο PriceTotaller έχει τις δυο μεταβλητές για να κρατήσει "κατάσταση". Αρκεί να δώσουμε τη λειτουργία AddBookToTotal().



   class PriceTotaller
   {
      int countBooks = 0;
      decimal priceBooks = 0.0m;

      internal void AddBookToTotal(Book book)
      {
         countBooks += 1;
         priceBooks += book.Price;
      }

      internal decimal AveragePrice()
      {
         return priceBooks / countBooks;
      }
   }

Και πράγματι στην Main() (αυτή τη λειτουργία εκτελεί στην αρχή ένα πρόγραμμα σε C#), φτιάχνουμε ένα νέο PriceTotaller() αντικείμενο ως totaller και μετά καλούμε την bookDB.ProcessPaperbackBooks() με παράμετρο ένα νέο αντικείμενο ProcessBookDelegate() το οποίο ζητάει παράμετρο όχι το Book που δείχνει η γραμμή:
public delegate void ProcessBookDelegate(Book book);
αλλά την totaller.AddBookToTotal η οποία για να εκτελεστεί πρέπει να έχει μια Book τυπική παράμετρο.

Η bookDB.ProcessPaperbackBooks θα στείλει κάθε βιβλίο μέσω του αντιπροσώπου (delegate) στη λειτουργία totaller.AddBookToTotal () του totaller οπότε μετά το πέρας εκτέλεσης τη εντολής με την λειτουργία που επιστρέφει τιμή την totaller.AveragePrice()) θα διαβάσουμε τη μέση τιμή των βιβλίων.


PriceTotaller totaller = new PriceTotaller();
         // Create a new delegate object associated with the nonstatic
         // method AddBookToTotal on the object totaller:
         bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));
         Console.WriteLine("Average Paperback Book Price: ${0:#.##}", totaller.AveragePrice());





Σε κάθε υπολογιστή με Windows, υπάρχει ήδη ο μεταφραστής της C#. Έτσι το πρόγραμμα που αναφέρω στο προηγούμενο μήνυμα μπορούμε να το τρέξουμε. Και σε Linux τρέχει, αρκεί να έχουμε περάσει στο Wine το .Net 3.5

Επισυνάπτω ένα αρχείο zip (testCsharp.zip)που περιέχει έναν φάκελο με δυο αρχεία
το ένα είναι ο πηγαίος κώδικας για το πρόγραμμα σε c#, το bookstore.cs
το άλλο είναι ένα bat αρχείο που περιέχει αυτό (το βλέπουμε με δεξί κλικ και επιλογή το edit)

setlocal
set PATH=%WINDIR%\Microsoft.NET\Framework\v3.5\;
set LIB=%WINDIR%\Microsoft.NET\Framework\;
csc.exe  /target:exe /out:first.exe bookstore.cs
first.exe > out.txt
endlocal

Μόλις το τρέξουμε μας δίνει δυο αρχεία, το first.exe, το εκτελέσιμο από το πηγαίο κώδικα, και το out.txt την έξοδο του προγράμματος:

Paperback Book Titles:
The C Programming Language
The Unicode Standard 2.0
Dogbert's Clues for the Clueless
Average Paperback Book Price: $23,97


 (προηγούμενο)

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

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

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