Τετάρτη, 6 Φεβρουαρίου 2019

Αναθεώρηση 13 Έκδοση 9.7

Διαγράφηκε κώδικας που εμφάνιζε λάθος ενώ δεν υπήρχε. Δηλαδή κάπου έγινα υπερβολικός πιστεύοντας ότι το κομμάτι των τεσσάρων γραμμών θα εμφάνιζε μήνυμα λάθους για μια κατάσταση, η οποία όμως δεν ήταν ανεπιθύμητη, και αφορούσε το κενό μεταξύ του χειριστή "=>" και ότι ακολουθεί.
Παρακάτω είναι κώδικας που δείχνει ό,τι επιλύθηκε το πρόβλημα (με αγγλικές εντολές):
Το πρόβλημα που έδινε η προηγούμενη έκδοση ήταν για το κενό μεταξύ του => και του SAY "OK", πράγμα που δεν μας ενδιαφέρει αφού γίνεται η δουλειά μας με και χωρίς κενό!

GROUP ALFA {
 GROUP BETA
 MODULE TEST_BETA {
  .BETA  =>  SAY "OK"
 }
}
CLASS BEING {
 MODULE SAY (A$) {
  PRINT A$
 }
}
Z=BEING()
\\ pointer show the names group (is a weak reference inside)
ALFA.BETA->Z
ALFA.TEST_BETA
\\ pointer show the float group (is a real pointer)
ALFA.BETA->(Z)
ALFA.TEST_BETA

Εκτός όμως από τα προβλήματα, σήμερα θα δείξω κάτι για την Λάμδα συνάρτηση:

Παρακάτω είναι ένα πρόγραμμα που δείχνει πώς η λάμδα συνάρτηση είναι τύπου τιμής και όχι αναφοράς, αλλά ως μέλος ομάδας (group) για την ομάδα είναι εσωτερικά τύπου αναφοράς (δηλαδή με δείκτη), που σημαίνει ότι αν αντιγράψουμε την Α στη Β και η Α έχει μια λάμδα συνάρτηση αυτή θα αντιγραφεί στο Β ως δείκτης, και έτσι τα οποιαδήποτε κλεισίματα (closures) θα είναι κοινά. Τα κλεισίματα (closures) στις λάμδα της Μ2000 είναι αντίγραφα την ώρα που δημιουργείται η λάμδα και παραμένουν εκεί και για τον κώδικα της λάμδα όταν τον καλούμε άμεσα αλλά και για την περίπτωση της αναδρομικής κλήσης, δηλαδή η λάμδα να καλέσει τον εαυτό της. Επειδή δεν μπορούμε να γνωρίζουμε το όνομα της συνάρτησης λάμδα (οι λάμδα είναι ανώνυμες ουσιαστικά), χρησιμοποιούμε το Λάμδα() ή το Λάμδα$() (lambda(), lambda$()) ως ονόματα για την κλήση αυτή. Τα ίδια ονόματα μπορούν να χρησιμοποιηθούν και σε απλές συναρτήσεις (επειδή και οι απλές έχουν τρόπο να μεταβιβαστούν με αναφορά, και έτσι πάλι χάνεται το αρχικό όνομα της συνάρτησης).


Μια μικρή εξήγηση του κώδικα. Πρώτα ορίζουμε την α ως την τιμή της lambda... (η λάμδα είναι ανώνυμη συνάρτηση και έτσι αυτό που πάει στην a είναι ένας δείκτης στην συνάρτηση, αυτό το λέμε αλλιώς ότι η a είναι αντικείμενο τύπου lambda.

Η λάμδα κατασκευάζεται με τέσσερα μέρη. Πρώτο μέρος είναι το αναγνωριστικό lambda (λάμδα) ή lambda$ (λάμδα$). Αυτό το μέρος λέει στον διερμηνευτή τι γυρίζει η λάμδα, από τα δυο: αριθμό ή αλφαριθμητικό. Μπορεί όμως να γυρίζει αντικείμενο, αλλά σημασία έχει το τι μπορεί να σταθεί αριστερά του ίσον. Εδώ έχουμε για αριθμούς και η μεταβλητή a μπορεί να δεχτεί αριθμούς άρα είμαστε εντάξει. Το δεύτερο μέρος είναι μια λίστα κλεισιμάτων. Η λίστα δεν έχει παρενθέσεις, και τα κλεισίματα μπορούν να έχουν ή να μην έχουν κάποια αρχική τιμή. Όταν δεν βάζουμε αρχική τιμή τότε πρώτα κοιτάει ο διερμηνευτής αν αυτό που έχουμε για κλείσιμο υπάρχει, αν ναι τότε το αντιγράφει, αν όχι το δημιουργεί με αρχικές τιμές. Δεν μπορούμε να περάσουμε πχ ένα πίνακα Α() χωρίς αυτός ήδη να υπάρχει. Μπορούμε όμως να περάσουμε αυτόματο πίνακα, πχ b=(1,2,3,4,5) θα κάνει το b ως κλείσιμο με δείκτη σε πίνακα πέντε στοιχείων. Μετά τα κλεισίματα  ακολουθεί μια λίστα παραμέτρων σε παρενθέσεις. Αυτό είναι προαιρετικό. Τόσο όσον αφορά το αν θέλουμε εισαγωγή παραμέτρων ως και σε όσον αφορά το πώς θα διαβάσουμε τις παραμέτρους.  Όταν έχουμε τη λίστα παραμέτρων τότε ο διερμηνευτής βάζει μια πρώτη γραμμή στο κώδικα ως Διάβασε και ότι ακολουθεί στη λίστα. Θα μπορούσαμε δηλαδή αντί να βάλουμε την λίστα με τις παραμέτρους πριν το βελάκι ->, να βάλουμε μια ή περισσότερες Διάβασε εντός του κώδικας. Ο κώδικας πάει μετά το βελάκι, ή ως μια παράσταση ή ως ένα μπλοκ με πολλές εντολές. Γενικά αν βάλουμε μια παράσταση ο διερμηνευτής φτιάχνει το μπλοκ και βάζει ένα = πριν την παράσταση. Οι συναρτήσεις στην Μ2000 επιστρέφουν τιμή με ένα = ως εντολή. Εδώ έχουμε μπλοκ και το = γυρνάει το χ. Μετά ακολουθεί η άνω και κάτω τελεία που δηλώνει ότι θα ακολουθήσει νέα εντολή και εκεί αυξάνουμε το χ κατά 2. Τα κλεισίματα στις Λάμδα στην Μ2000 μπορούν να αλλάξουν στις λάμδα, απλά η λάμδα δεν τα προσφέρει ως μέλη όπως κάνουν οι ομάδες. Τρίτο μέρος θεωρείται η λίστα παραμέτρων και τέταρτο μέρος ο κώδικας της συνάρτησης. Αν έχουμε ως παραμέτρους διαδοχικά πολλές λάμδα, τότε εκτός από την τελευταία τις άλλες τις βάζουμε σε παρενθέσεις (εκτός και όλα τα τέταρτα μέρη είναι μπλοκ). Αυτό συμβαίνει γιατί σε αυτήν την υλοποίηση της Μ2000 πριν την εκτέλεση μιας παράστασης ο διερμηνευτής με έναν γρήγορο τρόπο βγάζει συμπέρασμα αν είναι αλφαριθμητική ή αριθμητική η παράσταση που ακολουθεί για να καλέσει τις αντίστοιχες εσωτερικές δικές του συναρτήσεις.

Μετά ακολουθεί η δημιουργία ενός αντικειμένου χρήστη, η λεγόμενη ομάδα (Group). Εδώ  η ομάδα έχει όνομα G, και θα υπάρχει όσο το τμήμα που φτιάχτηκε συνεχίζει να εκτελείται. Σε αυτήν ορίζουμε μια ομάδα k με ένα μόνο αποτέλεσμα το 0 (δεν βάζουμε το =, θα το βάλει ο διερμηνευτής).
Μέλη στην ομάδα λέμε τις μεταβλητές και τους πίνακας. Η  λάμδα αφού έχει μεταβλητή είναι μέλος. Ουσιαστικά μέλη είναι και τα τμήματα (modules) και οι συναρτήσεις (functions) αλλά δεν τα λέμε μέλη, τα λέμε μεθόδους. Η λέξη ιδιότητα δηλώνει ένα είδος μέλους που λέγεται ιδιότητα, και είναι ουσιαστικά μια ομάδα που γυρνάει τιμή ή παίρνει τιμή ή κάνει και τα δύο, και μπορούμε να ελέγχουμε τι παίρνουμε και τι δίνουμε (σε άλλες γλώσσες αυτό θα ήταν ένα αντικείμενο με getters και setters).

Οπότε ξεκινάμε να δείξουμε ότι κάθε φορά που καλούμε την a() αυτή δίνει την τιμή του x και τον αυξάνει κατά 1. Μετά εισάγουμε στην G.k που είναι λάμδα την a. Ο διερμηνευτής βλέπει τις λάμδα ως πρώτους πολίτες (first citizen), που σημαίνει ότι λειτουργούν όπως οι μεταβλητές, παίρνουν και δίνουν αντίγραφο της τιμής τους. Εδώ το αντίγραφο θα είναι μαζί με τα αντίγραφα των κλεισιμάτων.

Επίσης φτιάχνουμε την ομάδα G1 ως αντίγραφο της G. Θα δούμε μετά γιατί το κάνουμε αυτό.

Για να δείξουμε ότι όντως έχουμε αντιγραφή του κλεισίματος x, αρκεί να τρέξουμε πρώτα την a() δυο φορές, ώστε να έχουμε τα 3 και 4 ως αποτέλεσμα και μετά να καλέσουμε στο G το τμήμα test_me το οποίο συγκρίνει μια τιμή που δίνουμε, τα 3 και 4 και αν συμπίπτει με την τιμή της λάμδα συνάρτησης k() (δείτε τη τελεία πριν το k στο κώδικα, λέει ότι το k είναι μέλος της ομάδας).

Μετά θέλουμε να δείξουνε ότι στην αντιγραφή ομάδας σε νέα ομάδα, η λάμδα ως μέλος θα αντιγραφεί ως δείκτης και όχι ως τιμή. Αρκεί να καλέσουμε την ίδια μέθοδο στο δεύτερο αντικείμενο, στο G1. Αν ήταν περασμένη με τιμή θα έπρεπε να ξεκινήσει η αρίθμηση από το 3 (αφού τότε εκεί ήταν η k του G. Όμως βλέπουμε ότι θα πάρουμε τιμές 4 και 5, άρα έχουμε αντιγραφή με δείκτη που δείχνει την ίδια λάμδα. Ο δείκτης είναι ένα αντικείμενο φορέας που περιέχει ένα δείκτη στην λάμδα. Αυτός ο φορέας είναι θεατός μόνο από τον διερμηνευτή. Αν τώρα βάλουμε πάλι στο k του G την τιμή του a, τότε θα δείξουμε ότι τα G και G1 δεν έχουν πια κοινή λάμδα! Αυτό πάλι είναι εύκολα κατανοητό εφόσον τρέξουμε την k του G που θα μας δώσει το 5 (αλλιώς θα μας έδινε το 7), και όντως η k του G1 δίνει 7. Επίσης βλέπουμε ότι η a δεν έχει αλλάξει και ότι αν καλέσουμε την συνάρτηση a() δυο φορές θα πάρουμε τα 5 και 4.



a=lambda x=1 ->{
 =x : x++
}
Group G {
 k=lambda ->0
 module test_me (n) {
  Print .k()=n
 }
}
Print a()=1, a()=2
\\ G.k get a copy of a
G.k=a
\\ G1.K get a reference of G.K
G1=G
Print a()=3, a()=4
G.test_me 3
G.test_me 4
\\ this shows that G1 get G.k by reference
G1.test_me 5
G1.test_me 6
\\ reloading G.k as a copy of a, so now G.k has a new pointer to a new copy of a
G.k=a
G.test_me 5
G1.test_me 7
Print a()=5, a()=6

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

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