Χαρακτηριστικό των λάμδα συναρτήσεων είναι ότι μπορούν να έχουν δικές τους μεταβλητές, που πήραν (σύλληψη, capture) την στιγμή της δημιουργίας τους, ή είναι αντίγραφο από άλλη λάμδα συνάρτηση.
Ενώ μπορούμε να έχουμε αντικείμενα με ότι επιμέρους θέλουμε, π.χ. πίνακες, συναρτήσεις, κ.α., και να τα στέλνουμε σε κλήση ως παράμετρο, ή να τα επιστρέφουμε ως αποτέλεσμα, το "κλείσιμο" ή closure, όπως λέγεται, που έχει η λάμδα στη Μ2000, απομονώνει τα στοιχεία που κλείνει και τα διαθέτει με το τρόπο που ορίζεται από το σώμα της, και μάλιστα μπορεί να το κάνει σταδιακά.
Τεχνικά η Μ2000 χρησιμοποιεί ένα αντικείμενο εσωτερικά που έχει δυο μέρη. το σώμα της συνάρτησης και μια λίστα με ότι έχει κλείσει. Κάθε φορά που την χρησιμοποιούμε, κάθε φορά που τη καλούμε,...κάνει τοπικά τα στοιχεία που έκλεισε, , και όταν τερματίσει τα ξανακλείνει.
Οι λ συναρτήσεις αντιγράφονται όπως οι μεταβλητές.
Οι μεταβλητές που μπορούμε να κλείσουμε είναι απλές μεταβλητές, ομάδες, πίνακες και άλλες λ συναρτήσεις. (ομάδες και πίνακες μπήκαν σε αυτή την αναθεώρηση).
Οι πίνακες στη Μ2000 πρέπει να ορίζονται πριν τους χρησιμοποιήσουμε.
Dim A(10)=5, B()
Arr=lambda A() -> A()
Arr1=lambda A() -> A(Number)
Dim A()
B()=Arr()
Print B(4), Arr1(3)
Στο παραπάνω παράδειγμα, φτιάχνουμε ένα πίνακα Α() με δέκα στοιχεία με τον αριθμό 5.
Μετά φτιάχνουμε δυο λάμδα συναρτήσεις οι οποίες και οι δύο κρατούν ένα αντίγραφο του Α(). Στην τέταρτη εντολή μηδενίζουμε τα στοιχεία του Α(). Το λάμδα Arr επιστρέφει αντίγραφο του Α() ενώ το το λάμδα Arr1 επιστρέφει στοιχείο του πίνακα. Σε καμία περίπτωση δεν μπορούμε να πειράξουμε τους πίνακες που έχουν κλείσει εδώ οι λ, όπως είναι δηλωμένες.
μπορούμε να δώσουμε στην Arr έναν άλλο ορισμό π.χ.
Arr=lambda->0
Ο πίνακας που κράταγε διαγράφεται. Αυτό συμβαίνει γιατί το Arr θα πάρει ένα νέο αντικείμενο λάμδα, και το παλιό θα χαθεί (θα διαγραφεί)
Ακόμα και αν δεν το κάναμε έτσι, με το που θα τερμάτιζε το τμήμα ή η συνάρτηση που δημιουργήσαμε την Arr θα διαγραφεί..εκτός και αν την επιστρέψουμε με κάποιο τρόπο.
Function FactoryB {
Read &A()
=lambda A() -> A(number)
Dim A()
}
Dim B(10)=100
M=FactoryB(&B())
Print M(3) \\ function not array
Print Dimension(B()) \\ 0
Στο παραπάνω παράδειγμα δημιουργούμε ένα πίνακα Β() και τον περνάμε με αναφορά σε μια συνάρτηση που ονομάσαμε FactoryB. Σε αυτήν δίνουμε ως αποτέλεσμα μια λ συνάρτηση με τον πίνακα κλειστό σε αυτήν και μηδενίζουμε τον Β() (μηδενίζουμε το Α() που είναι αναφορά του Β(), άρα μηδενίζουμε το Β()).
Μετά τυπώνουμε το τέταρτο στοιχείο του πίνακα (από 0 ξεκινάει η αρίθμηση στοιχείων). Και μετά τυπώνουμε τον αριθμό διαστάσεων του Β() (είναι 0).
Τώρα στον πίνακα μέσα στην Μ() δεν μπορούμε να επέμβουμε, είναι κλειστός.
M=Lambda ->{ Read &A() : =lambda A() -> A(number) : Dim A() }
Module Some {
Dim B(10)=100
Read &factoryB
FactoryB= FactoryB(&B())
}
Call Some &M \\ Call is optional for Modules
\\ Now M change definition
Print M(4) \\ 100
Στο παραπάνω παράδειγμα, έχουμε την Μ ως λάμδα που δίνει μια λάμδα επίσης. Την περνάμε με αναφορά στο Some και στην επιστροφή έχει αλλάξει, και μας δίνει τον πίνακα που κράτησε.
Σε αυτή τη γραμμή FactoryB= FactoryB(&B()) έχουμε κάνει την αλλαγή του λάμδα με το νέο λάμδα που έδωσε...το ίδιο!
Dim A$(10)="ok", B$()
Arr$=lambda$ A$() -> A$()
Dim A$() \\ erase A$() , but A$() captured from Arr$
B$()=Arr$()
Print B$(4) \\ Ok
Dim A$(10)="Yes"
\\ we change function to Arr$ (feed again new array)
Arr$=lambda$ A$() -> A$()
B$()=Arr$()
Print B$(4) \\ Yes
Στο παραπάνω παράδειγμα έχουμε λάμδα αλφαριθμητικό. Επίσης βλέπουμε ότι μπορούμε να δώσουμε νέο ορισμό κλείνοντας εκ νέου τον ίδιο πίνακα με νέες τιμές.
Με τα λάμδα μπορούμε να κατασκευάζουμε αντικείμενα σε δύο ή περισσότερα στάδια.
Function GroupFactory {
group alfa {
name$, x
Dim A()
}
Read alfa.name$
=lambda Alfa -> {
For alfa {
Read .x
dim .A(.x)
\\ do some job here
For i=0 to .x-1 {.A(i)=i }
}
=Alfa
}
}
\\ first stage
Factory=GroupFactory("My Class")
\\ second stage
AA=Factory(10)
For i=0 to 9:Print AA.A(i): next i
Print AA.name$
Στο παραπάνω πρόγραμμα κατασκευάζουμε μια Factory λάμδα που δέχεται μια παράμετρο, τον αριθμό των στοιχείων, και δίνει ένα αντικείμενο. Θα μπορούσαμε να το γράψουμε σε ένα στάδιο χωρίς χρήση λάμδα:
Class GroupFactory {
name$, x
Dim A()
Module GroupFactory {
Read .name$, .x
dim .A(.x)
\\ do some job here
For i=0 to .x-1 {.A(i)=i }
}
}
\\ first/second stage
AA=GroupFactory("My Class",10)
For i=0 to 9:Print AA.A(i): next i
Print AA.name$
Η Class ή Κλάση είναι συνάρτηση που δίνει αντικείμενο. Τα αντικείμενα στην Μ2000 χωρίζονται στο βασικό και τα ειδικά. Τα ειδικά είναι το EditBox (στοιχείο για φόρμες) , το Document, και άλλα. ενώ το βασικό είναι το Group ή Ομάδα. Το βασικό δεν έχει άλλο τύπο. Μπορούμε να βάλουμε ότι θέλουμε, ακόμα και λάμδα συνάρτηση. Ο τύπος της ΑΑ δίνεται με το Type$(AA) και βγαίνει Group. Αν μας ενδιαφέρει να έχουν τύπο ιδιαίτερο οι ομάδες μας, τότε βάζουμε μια ιδιότητα που γράφει το τύπο! Αν ΑΑ είναι ομάδα και ΒΒ είναι ομάδα το ΑΑ=ΒΒ δεν είναι εκχώρηση αλλά συγχώνευση στο ΑΑ, δηλαδή το ΑΑ γίνεται ίδιο με το ΒΒ σε ότι έχει το ΒΒ, αλλά το ΑΑ μπορεί να έχει και άλλα.
Class aa {
X=lambda p ->{ p++: =p}
}
dim a(10)=aa()
For k=1 to 10
For a(1) {
Print .x()
}
Next k
Στο παραπάνω πρόγραμμα δημιουργούμε ένα πίνακα με αντικείμενα τα οποία έχουν μόνο μια συνάρτηση λάμδα. Κάθε λάμδα συνάρτηση έχει ένα p και κάθε φορά που την καλούμε μας δίνει το νούμερο αφού το αυξήσει κατά ένα. Σε πίνακες οι ομάδες είναι κλειστές. Ανοίγουν για λίγο και κλείνουν μετά. Εδώ ανοίγουν στην For {}. Αν το Α(1) έχει ομάδα τότε το Α(1)=ΑΑ() θα του δώσει νέα ομάδα και θα πετάξει την παλιά (άρα το p θα είναι μηδέν σίγουρα). Στους πίνακες δεν έχουμε συγχώνευση αλλά αντικατάσταση αντικειμένων.
Group Alfa {
x=40
}
Dim A(10)
For i=0 to 9
A(i)=lambda alfa -> {
=alfa.x
if alfa.x>1 then alfa.x--
}
Next i
Print A(1)()
Alfa.x=10 \\ no change for x in alfa in lambda
For k=1 to 800
Print string$("*",A(random(0,9))())
Next k
Στο παραπάνω πρόγραμμα περνάμε σε ένα πίνακα από ένα λάμδα με ένα αντικείμενο (θα μπορούσε να είχε ότι θέλουμε μέσα, εδώ έχει μόνο μια ιδιότητα x). Τυπώνει 800 φορές γραμμές με αστεράκια που προοδευτικά λιγοστεύουν μέχρι να γίνουν ένα! Απλά το πρόγραμμα επιλέγει κάθε φορά μια τυχαία συνάρτηση, και ζητάει ένα νούμερο, αυτό έρχεται από το alfa.x και παράλληλα μειώνεται.
Στους πίνακες καλούμε την συνάρτηση λάμδα με διπλή παρένθεση (η πρώτη είναι για τις διαστάσεις, μέχρι δέκα, και η δεύτερη για τις παραμέτρους). Στις συναρτήσεις η Μ2000 δεν γνωρίζει πόσες παραμέτρους περνάμε. Περνάμε όσες θέλουμε και ζητάμε μέσα από τη συνάρτηση, και αν τις βρούμε όπως τις ζητάμε τότε έχει καλώς, αλλιώς έχουμε λάθος. Υπάρχουν εντολές που ελέγχουμε τι παίρνουμε. Και παραπάνω να δώσουμε δεν έχει πρόβλημα, τις επιπλέον θα τις διαγράψει στο τέλος της κλήσης.
Το ίδιο πρόγραμμα γράφεται χωρίς λάμδα μάλλον πιο ωραία!
Group Alfa {
Dim Ar(10)=40
Function A {
Read index
=.Ar(index)
if .Ar(index)>1 then .Ar(index)--
}
}
For k=1 to 800
Print string$("*",Alfa.A(random(0,9)))
Next k
Μια καλή χρήση είναι όμως η παραγωγή κατά παραγγελία συναρτήσεων. Εδώ έχουμε μια συνάρτηση Factory που δίνει έξι διαφορετικές συναρτήσεις, με κοινή βάση, την μέτρηση από ένα νούμερο μέχρι ένα όριο (που δεν το πιάνει), και με βήμα που δίνουμε. Επιλέγουμε βήμα ή την μονάδα, επιλέγουμε το τέλος να μας το δίνει σε μεταβλητή που περνάμε με αναφορά, ή με λάθος, και κάνει έλεγχο αν έχουμε κάτω της μονάδας Το Try {} κρατάει τα λάθη εντός. Η εντολή loop σημαίνει ένα flag στο block {} και όταν φτάσει στο τέρμα του γυρνάει πάλι από την αρχή (σχεδόν όλα τα μπλοκ μπορούν να γίνουν μπλοκ με επανάληψη με τη χρήση του Κυκλικά ή loop)
\\ 6 different functions from Factory Function
Function Factory {
Read X, T
if X>T Then Swap X,T
Let S=1, Er$=""
If match("N") then Read S
If match("S") then Read Er$
If S=1 then {
if Er$="" Then {
=lambda X, T -> {
read &finish
=X: X++ : if X>T then finish=false
}
} Else {
=lambda X, T, Er$ -> {
if X>T then Error Er$
=X: X++
}
}
} Else.if S>1 then {
if Er$="" Then {
=lambda X, T,S -> {
read &finish
=X: X+=S : if X>T then finish=false
}
} Else {
=lambda X, T,S, Er$ -> {
if X>T then Error Er$
=X: X+=S
}
}
} Else {
If Er$="" then {
=lambda X -> {Read &Finish : Finish=false : =X}
} else {
=lambda X, P=2-> {
=X : P--
If P=0 Then Error "Nothing"
}
}
}
}
Print "Using Error"
B=Factory(5,10, "full")
Try { Print B() : loop }
Print "Using Flag"
B=Factory(5,10)
k=true
While k {Print B(&k)}
Print "Using Step"
B=Factory(5,10,2, "full")
Try { Print B() : loop }
Print "Using Flag"
B=Factory(5,10,2)
k=true
While k {Print B(&k)}
Print "Using 0 for step"
B=Factory(5,10,0,"full")
try {Print B() : loop}
Print "Using 0 for step, with flag"
B=Factory(5,10,0)
k=true
While k {Print B(&k)}
Το ComboBox...θα το δούμε στην επόμενη ανάρτηση
2023
Ομάδα Αλφα1 {Μια χρησιμότητα του τύπου είναι να περιορίσει την εισαγωγή παραμέτρου σε μια συνάρτηση, θέτοντας στον ορισμό της λίστας ορισμάτων τον επιθυμητό τύπο. Στο παράδειγμα χρησιμοποιούμε μια απλή συνάρτηση (γράφεται στο τέλος του τμήματος/συνάρτησης/λάμδα συνάρτησης) και καλείται με το @ στην αρχή. Η διαφορά αυτών των συναρτήσεων είναι ότι δεν διαθέτουν δικό τους όνομα χώρου, που σημαίνει ότι "βλέπουν" ότι έχει το τμήμα. Παρόλα αυτά ότι νέο δημιουργούν το σβήνουν. Επίσης ο σωρός τιμών είναι ο ίδιος με αυτόν που την καλεί, που σημαίνει ότι μια συνάρτηση μπορεί να αφήσει αλλαγές στο σωρό τιμών αν θέλουμε!
Τύπος: Αλφα
χ=10
}
Τύπωσε Αλφα1 είναι τύπος Αλφα
? @Κάππα(Αλφα1)=10 // ? είναι
Συνάρτηση Κάππα(χ ως Αλφα)
=χ.χ
Τέλος Συνάρτησης
// Κληρονομικότητα μέσω κλάσεων:
Κλάση Βήτα {
χ=10
}
Κλάση Δέλτα ως Βήτα {
ψ=30
}
Ζ=Δέλτα()
Τύπωσε Ζ.χ, Ζ.ψ
Τύπωσε Ζ είναι τύπος Βήτα
Τύπωσε Ζ είναι τύπος Δέλτα
// κληρονομικότητα μέσω αντικειμένων
Ομάδα Κάππα {
Τύπος: Άλλο
Μ=100
}
ΚάππαΒήτα=Κάππα με Βήτα()
Τύπωσε ΚάππαΒήτα.χ, ΚάππαΒήτα.Μ
Τύπωσε ΚάππαΒήτα είναι τύπος Άλλο
Τύπωσε ΚάππαΒήτα είναι τύπος Βήτα
Τύπωσε @ΤιμήΧ(&Ζ)=10
Τύπωσε @ΤιμήΧ(&ΚάππαΒήτα)=10
Συνάρτηση ΤιμήΧ(&Ζ ως Βήτα)
=Ζ.χ
Τέλος Συνάρτησης
Επίσης στις νεότερες εκδόσεις έχουμε δείκτες σε αντικείμενα και ισχύουν και εδώ οι τύποι. Δείτε ότι τα Ζ και ΚάππαΒήτα είναι δείκτες σε αντικείμενα, και όχι αντικείμενα. Σε κάποιες γλώσσες τα αντικείμενα είναι χηρίσιμα πάντα μέσω δεικτών. Στη Μ2000 οι ομάδες είναι σαν μεταβλητές, αλλάζουν τιμή με εκχώρηση νέας ομάδας σε υπάρχουσα, με συγχώνευση. Αν θέλουμε όμως φτιάχνουμε δείκτες σε ομάδες και τότε η εκχώρηση τιμής θέλει τιμή τύπου δείκτη σε ομάδα. Η Δείκτης(Κ) όπου Κ μια ομάδα γυρνάει δείκτη που εσωτερικά είναι ισχνή αναφορά. Η Δείκτης((Κ)) γυρνάει δείκτη σε αντίγραφο της Κ, και εδώ είναι "πραγματικός" δείκτης. Ο πραγματικός δείκτης κρατάει "ζωντανή" την ομάδα. Σε κάθε άλλη περίπτωση μια ομάδα διαγράφεται όταν βγει "εκτός σκοπού". Για παράδειγμα στη συνάρτηση Κάππα() στο πιο προηγούμενο παράδειγμα, φτιάχνει μια νέα μεταβλητή τύπου ομάδας ως χ και παράλληλα φτιάχνει και ότι έχει η ομάδα που περνάμε με αντιγραφή (με τιμή) όπως τη χ μεταβλητή, και στο τέλος εκτέλεσης διαγράφονται. Στη ΤιμήΧ() του προηγούμενου παραδείγματος περνάμε με αναφορά ομάδα και ο διερμηνευτής φτιάχνει νέα ονόματα, όπου όλα είναι αναφορές στις μεταβλητές της ομάδας, εκτός από την μεταβλητή της ομάδας που φτιάχνει αντίγραφο (για το λόγο αυτό αν αλλάξουμε δομή, δηλαδή αν προσθέσουμε συναρτήσεις/μεθόδους στη περασμένη με αναφορά ομάδα, δεν θα πειραχθεί η ομάδα που περάσαμε με αναφορά, αλλά αν αλλάξουμε τιμές στις "γνωστές" μεταβλητές, επειδή είναι περασμένες με αναφορά θα αλλάξουν). Εδώ δεν μπορούμε να αλλάξουμε δείκτη, επειδή η περασμένη με αναφορά έχει έναν "κρυφό" δείκτη και διαφορετικό από αυτήν στη συνάρτηση που έγινε "αναφορά" της, και είναι άγνωστος στη συνάρτηση. Στην ΤιμήΧ() αυτού του παραδείγματος περνάμε με δείκτη μια ομάδα. Όταν έχουμε δείκτη σε ομάδα, τα στοιχεία της δεν είναι συνδεδεμένα με το σύστημα μεταβλητών/συναρτήσεων. Όταν όμως χρησιμοποιούμε το => τα βρίσκει ο διερμηνευτής γιατί φτιάχνει πρόσκαιρα μια ομάδα που έχει ότι έχει το αντικειμενο του δείκτη αν έχουμε πραγματικό δείκτη, ή δημιουργεί αναφορές αν έχουμε δείκτη τύπου ισχνής αναφοράς. Η διαφορά τύπου δείκτη είναι αδιαφανής για τον χρήστη, αλλά έχει επιπτώσεις: Αν για παράδειγμα επιστρέψουμε τιμή από συνάρτηση και είναι δείκτης σε ομάδα τύπου ισχνής αναφοράς, με αναφερόμενη ομάδα στη συνάρτηση, τότε η χρήση του δείκτη θα βγάλει λάθος γιατί η ισχνή αναφορά δεν θα μετασχιματίζεται σε πραγματική αναφορά (έχει χαθεί πια το αντικείμενο που δηλώνει η ισχνή αναφορά).
// Κληρονομικότητα μέσω κλάσεων:
Κλάση Βήτα {
χ=10
}
Κλάση Δέλτα ως Βήτα {
ψ=30
}
Ζ->Δέλτα()
Τύπωσε Ζ=>χ, Ζ=>ψ
Τύπωσε Ζ είναι τύπος Βήτα
Τύπωσε Ζ είναι τύπος Δέλτα
// κληρονομικότητα μέσω αντικειμένων
Ομάδα Κάππα {
Τύπος: Άλλο
Μ=100
}
ΚάππαΒήτα->(Κάππα με Βήτα())
Τύπωσε ΚάππαΒήτα=>χ, ΚάππαΒήτα=>Μ
Τύπωσε ΚάππαΒήτα είναι τύπος Άλλο
Τύπωσε ΚάππαΒήτα είναι τύπος Βήτα
Τύπωσε @ΤιμήΧ(Ζ)=10
Τύπωσε @ΤιμήΧ(ΚάππαΒήτα)=10
Συνάρτηση ΤιμήΧ(Ζ ως *Βήτα)
=Ζ=>χ
Τέλος Συνάρτησης
Τύπωσε @ΤιμήΧ1(&Ζ)=10
Τύπωσε @ΤιμήΧ1(&ΚάππαΒήτα)=10
Συνάρτηση ΤιμήΧ1(&Ζ ως *Βήτα)
=Ζ=>χ
Τέλος Συνάρτησης
Τμήμα Αλφα {
Συνάρτηση Δέλτα {
=αριθμός**2
}
Τύπωσε Δέλτα(2)=4
Τύπωσε &Δέλτα()
Τμήμα Κάτι (&κ()) {
Τύπωσε κ(3)=9
}
Κάτι &Δέλτα()
Λ=Λάμδα ->αριθμός^2
Τύπωσε &Λ()
Κάτι &Λ()
Ομάδα Ζήτα {
Ιδιωτικό:
ακέραιος κ
Δημόσιο:
Συνάρτηση Κλήσεις {
=.κ
}
Συνάρτηση Δέλτα {
=αριθμός**2
.κ++
}
}
Τύπωσε Ζήτα.Κλήσεις()=0
Τύπωσε &Ζήτα.Δέλτα()
Κάτι &Ζήτα.Δέλτα()
Τύπωσε Ζήτα.Κλήσεις()=1
}
Αλφα
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου
You can feel free to write any suggestion, or idea on the subject.