Professional Documents
Culture Documents
Γλώσσες Προγραμματισμού Ι Νικόλαος Παπασπύρου Κωστής Σαγώνας ΕΜΠ
Γλώσσες Προγραμματισμού Ι Νικόλαος Παπασπύρου Κωστής Σαγώνας ΕΜΠ
• Κύρια χαρακτηριστικά:
– Ανάθεση μεταβλητών (πολλαπλή)
– Επανάληψη
– Η σειρά εκτέλεσης παίζει σημαντικό ρόλο
• Κύρια χαρακτηριστικά:
– Μεταβλητές μιας τιμής
– Η επανάληψη εκφράζεται με χρήση αναδρομής
×/ιX
(Για την ακρίβεια, δε θα το γράφαμε με αυτό τον τρόπο στην APL, γιατί
η γλώσσα περιλαμβάνει το μοναδιαίο τελεστή παραγοντικό: !X)
Εισαγωγή στις Γλώσσες Προγραμματισμού 20
Assembly
COBOL
APL
SNOBOL, Icon
Β1 * Β2
Prolog, Mercury
append([],Ys,Ys).
append([X|Xs],Ys,[X|Zs]) :- append(Xs,Ys,Zs).
Διάλεκτοι
Προστακτική ML
class Fubar {
public static void main (String[] args) {
// όλο το πρόγραμμα εδώ!
}
}
Συναρτησιακή Pascal
Θέματα Σχεδιασμού
Γλωσσών Προγραμματισμού
Εν αρχή ην... η χακεριά
mailman:*:78:78:Mailman user:/var/empty:/usr/bin/false
• Κύριο πρόγραμμα
– Δηλώσεις αρχείων επικεφαλίδων
– Δηλώσεις μεταβλητών
#include <fstream>
#include <iostream>
#include <string>
• Άνοιγμα αρχείου
– Έλεγχος σφάλματος
infile.open("/etc/passwd");
if (!infile) {
cout << "Error opening /etc/passwd.\n";
exit(-1);
}
• Κύρια επανάληψη
infile.getline(newrecord, 256);
while (!infile.fail()) {
// -- Make array into a String
String urecord(newrecord);
if (urecord.find("#") == string::npos) {
// Process an entry
...
}
infile.getline(newrecord, 256);
}
return 0; // Done
}
StringTokenizer st =
new StringTokenizer(record, ":");
String username = st.nextToken();
st.nextToken(); // Don’t care about these
st.nextToken();
st.nextToken();
st.nextToken();
String home = st.nextToken();
String shell = st.nextToken();
#!/usr/bin/perl
Επίδοση σε ταχύτητα
• Θεωρητική θεμελίωση
– Τυπικές γλώσσες, λ-λογισμός, θεωρία τύπων, σημασιολογία
x = y/*p;
• Παραδείγματα:
– ’0’ – ταιριάζει μόνο με το χαρακτήρα 0 (μηδέν)
– ’0’|’1’ – ταιριάζει με μηδέν ή με ένα
– ’0’|’1’|’2’|’3’|’4’|’5’|’6’|’7’|’8’|’9’ – ταιριάζει με ψηφία
– [0-9] – το ίδιο με το παραπάνω αλλά σε πιο συμπαγή μορφή
– [0-9]* – σειρά από ψηφία (πιθανώς κενή)
Θέματα Σχεδιασμού Γλωσσών Προγραμματισμού 20
Παράδειγμα 1
while i < N do while (i < N)
begin {
i := i + 1 i = i + 1
end }
Pascal C/C++
Παράδειγμα 2
• Infix: 3 + (4 * 5)
• Prefix: (+ 3 (* 4 5))
• Postfix: 3 4 5 * +
Οι παραπάνω εκφράσεις έχουν
• Διαφορετική συγκεκριμένη σύνταξη +
“Consistently separating words by spaces became
a general custom about the tenth century A.D.,
and lasted until about 1957, when FORTRAN
abandoned this practice.”
– Sun FORTRAN Reference Manual
“C: a programming language which combines
the power of assembly language
with the flexibility of assembly language.”
“C is quirky, flawed, and a tremendous success.”
– Dennis Ritchie
• Έχουν τη μορφή
type-specifier init-declarator-list ;
declarator optional-initializer
Λεκτική εμβέλεια
int foo()
{
int b;
if (a == 0) {
printf("a was 0");
}
b = a; /* OK */
}
int bar()
{
a = 3; /* OK */
b = 2; /* Error: b out of scope */
}
Θέματα Σχεδιασμού Γλωσσών Προγραμματισμού 37
Εμβέλεια των external δηλώσεων
file1.c file2.c
int x; int boo()
{
int foo() foo(); /* Warning */
{ x = 2; /* Error */
bar(); /* Warning */ }
x = 1; /* OK */
} extern int foo();
extern int x;
int bar()
{ int baz()
foo(); /* OK */ {
} foo(); /* OK */
x = 3; /* OK */
}
Ο C Preprocessor
“Language design is library design.”
– Bjarne Stroustrup
<expr>
( <expr> )
<expr> * <expr>
( <expr> ) c
<expr> + <expr>
a b
κανόνας
παραγωγής <NP> ::= <A> <N>
τερματικά σύμβολα
(λεκτικές μονάδες)
Σύνταξη και Συντακτική Ανάλυση 6
Ορισμός γραμματικών σε μορφή Backus-Naur
Κανόνες παραγωγής
float a;
boolean a, b, c;
int a = 1, b, c = 1 + 2;
Σύνταξη και Συντακτική Ανάλυση 14
Παράδειγμα κατασκευής γραμματικής (2)
• Με ξεχωριστές γραμματικές
1. Μία που ορίζει πώς προκύπτουν οι λεκτικές μονάδες από ένα
αρχείο με χαρακτήρες
• Η γραμματική αυτή συνήθως είναι μια κανονική γραμματική (regular
grammar) και χρησιμοποιείται από το λεκτικό αναλυτή (scanner)
2. Μία που ορίζει πως προκύπτουν τα συντακτικά δένδρα από μία
ακολουθία λεκτικών μονάδων
• Η γραμματική αυτή συνήθως είναι μια γραμματική ελεύθερη
συμφραζομένων (context-free grammar) και χρησιμοποιείται από το
συντακτικό αναλυτή (parser)
Σύνταξη και Συντακτική Ανάλυση 19
• Τα παραπάνω
– προσθέτουν δυσκολία στην λεκτική και συντακτική ανάλυση και
– μειώνουν την αναγνωσιμότητα των προγραμμάτων
• Συντακτικά διαγράμματα
Σύνταξη και Συντακτική Ανάλυση 22
Παραδείγματα EBNF
if-stmt
if expr then stmt else stmt
• Πολλαπλοί κανόνες
exp + exp
παραγωγής χρησιμοποιούν
exp * exp
διακλαδώσεις (branching)
exp ( exp )
WhileStatement:
while ( Expression ) Statement
DoStatement:
do Statement while ( Expression );
ForStatement:
for ( ForInitopt ; Expressionopt ; ForUpdateopt)
Statement
<var> - <subexp>
b <var>
<subexp>
<subexp> - <var>
<var> b
a b
a + b * c
<exp>
<exp> + <exp>
G5 parse tree: <mulexp> <mulexp>
a <mulexp> * <mulexp>
b c
<exp> <exp>
b c a b
Παραδείγματα προσεταιριστικότητας
• C
a<<b<<c — τελεστές είναι αριστερά προσεταιριστικοί
a=b=0 — δεξιά προσεταιριστικός (ανάθεση)
• ML
3-2-1 — τελεστές είναι αριστερά προσεταιριστικοί
1::2::nil — δεξιά προσεταιριστικός (κατασκευή λίστας)
• Fortran
a/b*c — τελεστές είναι αριστερά προσεταιριστικοί
a**b**c — δεξιά προσεταιριστικός (ύψωση σε δύναμη)
<ex p>
<rootex p> b
e2 s1
Οι περισσότερες γλώσσες
που έχουν αυτό το πρόβλημα
< if-stm t> επιλέγουν το κάτω συντακτικό
δένδρο: το else πηγαίνει με το
if < ex p> th en < stmt>
κοντινότερο αταίριαστο then
e1 < if-stm t>
e2 s1 s2
<if-stmt>
e1 <if-stmt>
e2 s1 s2
int a=0;
if (0==0) Ποια είναι η τιμή του a
if (0==1) a=17; μετά την εκτέλεση του
else a=42; προγράμματος;
int a=0;
if (0==0)
if (0==1) a=17; Καλύτερο: σωστή στοίχιση
else a=42;
int a=0;
if (0==0) { Ακόμα καλύτερο: η χρήση
if (0==1) a=17; μπλοκ δείχνει καθαρά τη
else a=42; δομή του κώδικα
}
<exp> + <mulexp>
<rootexp>
<exp> + <mulexp>
<mulexp> <rootexp> c
<rootexp> b +
a
+ c
a b
Αφηρημένο συντακτικό δένδρο
Σύνταξη και Συντακτική Ανάλυση 52
Συμπερασματικά
• Συναρτησιακός προγραμματισμός
– Βασίζεται στο μαθηματικό μοντέλο του λ-λογισμού (Church)
– “Προγραμματισμός χωρίς μεταβλητές”
– Είναι από τη φύση του κομψός, σύντομος και σαφής τρόπος
προγραμματισμού, στον οποίο αποφεύγονται τελείως κάποιου
είδους προγραμματιστικά σφάλματα
– Θεωρείται από πολλούς ως ανώτερος τρόπος προγραμματισμού
• Διαχείριση μνήμης
– Στατική εμβέλεια και δομή κατά μπλοκ
– Εγγραφές ενεργοποίησης συναρτήσεων (function activation
records) και υλοποίηση συναρτήσεων υψηλής τάξης
% sml
Standard ML of New Jersey, v110.XX
- 42;
val it = 42 : int
- 2 + 3;
val it = 5 : int
- fun square x = x * x;
val square = fn : int -> int
- square 5;
val it = 25 : int
- square;
val it = fun : int -> int
• Booleans
– true, false : bool
- 1 = 2;
val it = false : bool
- 1 <> 2 andalso true <> false;
val it = true : bool
- true = false orelse 1 <= 2;
val it = true : bool
- "Robin" > "Milner";
val it = true : bool
- 2.56 < 3.14;
val it = true : bool
- 2.56 = 3.14;
stdIn: Error: operator and operand don’t agree
operator domain: ’’Z * ’’Z
operand: real * real
Εισαγωγή στη γλώσσα ML 11
Υπερφόρτωση τελεστών (operator overloading)
- 6 * 7
val it = 42 : int
- 6.0 * 7.0;
val it = 42.0 : real
- 2.0 * 21;
stdIn: Error: operator and operand don’t agree
operator domain: real * real
operand: real * int
in expression: 2.0 * 21
- fun max a b =
= if a > b then a else b;
val max = fn : int -> int -> int
- max 17 5;
val it = 17 : int
- max 10 42;
val it = 42 : int
Αναδρομή
val it = 10 : int
έννοια, τα προγράμματα χρησιμοποιούν αναδρομή για να
Επαναχρησιμοποίηση αποτελεσμάτων
fun f x =
let
val gg = g(square(max(x,4)))
in
gg + (if x < 1 then 1 else gg)
end;
- let
= val a = 2
= in
= (let
= val a = a + 2
= in
= a
= end,
= a)
= end;
val it = (4,2) : int * int
Λίστες
- null [];
val it = true : bool
- null [1,2];
val it = false : bool
- val l = [1,2,3,4];
val l = [1,2,3,4] : int list
- hd l;
val it = 1 : int
- tl l;
val it = [2,3,4] : int list
- length l;
val it = 4 : int
- nil;
val it = [] : ’a list
Εισαγωγή στη γλώσσα ML 25
Ορισμός συναρτήσεων για λίστες
- let
= val rec f =
= fn x => if null x then nil
= else (hd x + 3) :: f (tl x)
= in
= f
= end
= [1,2,3,4];
val it = [4,5,6,7] : int list
Περιεχόμενα
C: Java:
char byte (1-byte signed)
unsigned char char (2-byte unsigned)
short int short (2-byte signed)
unsigned short int int (4-byte signed)
int long (8-byte signed)
unsigned int Scheme:
long int integer
unsigned long int
Ακέραιοι “απείρου” εύρους
long long
Δεν υπάρχει προκαθορισμένη Haskell:
υλοποίηση, αλλά οι “μεγαλύτεροι” int (4-byte signed)
τύποι πρέπει να έχουν τουλάχιστον Integer (“άπειρο” εύρος)
το εύρος των “μικρότερων” τύπων
Εισαγωγή στους Τύπους 6
Θέματα σχεδιασμού
Κατασκευαζόμενοι τύποι
Πλειάδες (tuples)
Παράδειγμα: ANSI C
Ανύσματα (vectors)
type
LetterCount = array['a'..'z'] of Integer;
var
Counts: LetterCount;
begin
Counts['a'] = 1
…
Ενώσεις (unions)
datatype element =
I of int |
R of real;
fun getReal (R x) = x
| getReal (I x) = real x;
union element e;
e.i = 100;
double x = e.d;
Εισαγωγή στους Τύπους 22
Τι λέει η ANSI C για τις ενώσεις;
Υποσύνολα (subsets)
Λειτουργίες υποτύπων
Συναρτήσεις (functions)
Παράδειγμα: Lisp
Ισοδυναμία τύπων
var
Counts1: array['a'..'z'] of Integer;
Counts2: array['a'..'z'] of Integer;
Συμπερασματικά
Η γλώσσα ML σε βάθος
Τι σημαίνουν οι τύποι συναρτήσεων στην ML
• f : A → B σημαίνει:
– Για κάθε x ∈ A,
• Με λόγια:
“εάν η αποτίμηση f(x) τερματίσει κανονικά, τότε f(x)∈ B”
Η γλώσσα ML σε βάθος 2
Η γλώσσα ML σε βάθος 3
Παράδειγμα επισημειώσεων τύπων στην ML
Εκφράσεις case
- case 1+1 of
= 3 => "three" |
= 2 => "two" |
= _ => "hmmm...";
val it = "two" : string
• Έχουν τη σύνταξη:
<case-expr> ::= case <expression> of <match>
• Η έκφραση case της ML είναι μια πολύ ισχυρή δομή—και
αντίθετα με τις περισσότερες άλλες γλώσσες, μπορεί να
κάνει περισσότερα από απλή σύγκριση με σταθερές
Η γλώσσα ML σε βάθος 7
Παράδειγμα χρήσης case
case list of
_::_::c::_ => c |
_::b::_ => b |
a::_ => a |
nil => 0
Η γλώσσα ML σε βάθος 8
case exp1 of
true => exp2 |
false => exp3
Η γλώσσα ML σε βάθος 9
Αποτίμηση “βραχυκύκλωσης” στην ML
• Ερωτήσεις:
– Πόσο αποδοτική είναι η συνάρτηση reverse?
– Μπορούμε να αναστρέψουμε μια λίστα με ένα μόνο πέρασμα;
Η γλώσσα ML σε βάθος 13
Πιο αποδοτική συνάρτηση reverse
fun reverse xs =
let
fun rev (nil, z) = z
| rev (y::ys, z) = rev (ys, y::z)
in
rev (xs, nil)
end;
1 3
2 2 2 2
3 3 1 3 1 1
Η γλώσσα ML σε βάθος 14
Η γλώσσα ML σε βάθος 15
Η λέξη κλειδί op
- op *;
val it = fn : int * int -> int
- quicksort ([1,4,3,2,5], op <);
val it = [1,2,3,4,5] : int list
Η γλώσσα ML σε βάθος 16
Η γλώσσα ML σε βάθος 17
Πρακτική εξάσκηση
Η γλώσσα ML σε βάθος 19
Η συνάρτηση map
Η συνάρτηση foldr
Η γλώσσα ML σε βάθος 21
Παραδείγματα χρήσης foldr
- foldr (op +) 0 [1,2,3,4];
val it = 10 : int
- foldr (op * ) 1 [1,2,3,4];
val it = 24 : int
- foldr (op ^) "" ["abc","def","ghi"];
val it = "abcdefghi" : string
- foldr (op ::) [5] [1,2,3,4];
val it = [1,2,3,4,5] : int list
- foldr;
val it = fn : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
- foldr (op +);
val it = fn : int -> int list -> int
- foldr (op +) 0;
val it = fn : int list -> int
- val addup = foldr (op +) 0;
val addup = fn : int list -> int
- addup [1,2,3,4,5];
val it = 15 : int
Η γλώσσα ML σε βάθος 22
Η συνάρτηση foldl
Η γλώσσα ML σε βάθος 25
Ορισμοί τύπων δεδομένων
Η γλώσσα ML σε βάθος 26
• Παραδείγματα:
– datatype color = Red | Yellow | Blue
• στοιχεία : Red, Yellow, και Blue
– datatype atom = Atm of string | Nmbr of int
• στοιχεία : Atm("a"), Atm("b"), …, Nmbr(0), Nmbr(1), ...
– datatype list = Nil | Cons of atom * list
• στοιχεία : Nil, Cons(Atm("a"),Nil), …
Cons(Nmbr(2),Cons(Atm("ugh"),Nil)), ...
Η γλώσσα ML σε βάθος 27
Ορισμοί αναδρομικών τύπων δεδομένων
datatype ’d tree = Leaf of ’d
| Node of ’d * ’d tree * ’d tree;
1 2 6 7
Η γλώσσα ML σε βάθος 28
- val x = Value 5;
val x = Value 5 : exint
- x + x;
Error: overloaded variable not defined at type symbol: +
type: exint
• Ένα 'x bunch είναι είτε ένα πράγμα τύπου 'x, είτε μια
λίστα από πράγματα τύπου 'x
• Όπως συνήθως, η ML συμπεραίνει τύπους αυτόματα:
- One 1.0;
val it = One 1.0 : real bunch
- Group [true,false];
val it = Group [true,false] : bool bunch
Η γλώσσα ML σε βάθος 34
Η γλώσσα ML σε βάθος 35
Παράδειγμα: Μη πολυμορφικός συμπερασμός
Αυτή ήταν η ML
Η γλώσσα ML σε βάθος 38
Η γλώσσα ML σε βάθος 39
Συμπερασμός Τύπων
και Πολυμορφισμός
Συμπερασμός τύπων
• Έλεγχος τύπων
int f(int x) { return x+1; };
int g(int y) { return f(y+1)*2; };
– Κοιτάμε στο σώμα κάθε συνάρτησης χρησιμοποιώντας τις δηλώσεις
τύπων των μεταβλητών για τον έλεγχο της συνέπειάς τους
• Συμπερασμός τύπων
int f(int x) { return x+1; };
int g(int y) { return f(y+1)*2; };
– Κοιτάμε στον κώδικα, ο οποίος δεν περιέχει πληροφορία τύπων, και
«μαντεύουμε» ποιοι τύποι θα έπρεπε να είχαν δηλωθεί ώστε το
πρόγραμμα να είναι συνεπές ως προς τη χρήση των τύπων
Χρησιμότητα
• Συμπερασμός τύπων
– Θεωρείται ως μια από τις σημαντικότερες εξελίξεις στη θεωρία
και την πρακτική των γλωσσών προγραμματισμού
– Ο συμπερασμός τύπων της ML μας δίνει μια ιδέα
• του πώς δουλεύουν πολλοί άλλοι αλγόριθμοι συμπερασμού
τύπων αλλά και
• της στατικής ανάλυσης προγραμμάτων
• Παράδειγμα
- fun add2 x = 2+x;
val add2 = fn : int -> int
• Πώς συμπεραίνουμε τον παραπάνω τύπο;
– Ο + έχει δύο τύπους: int * int -> int
ή real * real -> real
– Η σταθερά 2 έχει τύπο int
– Αυτό σημαίνει ότι χρησιμοποιούμε τον τύπο : int*int -> int
– Αυτό με τη σειρά του σημαίνει ότι x:int
– Επομένως η συνάρτηση add2 έχει τύπο int -> int
:r (s = t→ r) :s→t
@ λ
f :s x :t x:s e :t
• Παράδειγμα
- fun f(g) = g(2); Γράφος για λg. (g 2)
val f = fn : (int -> ’a) -> ’a
s→t = (int→t)→t
• Πώς συμπεραίνεται ο τύπος; λ
Αναθέτουμε τύπους t (s = int→t)
@
στα φύλλα
Προωθούμε τύπους στους g:s 2 : int
εσωτερικούς κόμβους και
γεννάμε περιορισμούς
Επιλύουμε μέσω
αντικατάστασης
(ενοποίησης)
Συμπερασμός Τύπων και Πολυμορφισμός 10
Χρήση πολυμορφικών συναρτήσεων
• Συνάρτηση
- fun f(g) = g(2);
val f = fn : (int -> ’a) -> ’a
• Πιθανές χρήσεις
- fun add2(x) = 2+x;
val add2 = fn : int -> int
- f(add2);
val it = 4 : int
• Έστω η συνάρτηση:
- fun f(g) = g(2);
val f = fn : (int -> ’a) -> ’a
• Λάθος χρήση:
• Έστω η συνάρτηση
- fun f(g,x) = g(g(x));
val f = fn : (’a -> ’a) * ’a -> ’a
• Πολυμορφική συνάρτηση
- fun length nil = 0
= | length cons(x,rest) = 1 + length(rest);
val length = fn : ’a list -> int
• Συμπερασμός τύπων
– Συμπεραίνουμε κάποιο τύπο για κάθε πρόταση ξεχωριστά
– Συνδυάζουμε τους τύπους με τον περιορισμό ότι πρέπει να είναι
συμβατοί μεταξύ τους (οι τύποι ενοποιούνται αν αυτό είναι
αναγκαίο)
Συμπερασμός Τύπων και Πολυμορφισμός 14
Συμπερασμός τύπων και αναδρομή
• Η δεύτερη πρόταση:
length cons(x,rest) = 1 + length(rest)
int square(int x) {
return x*x;
}
double square(double x) {
return x*x;
}
double square(double x) {
square_d
return x*x;
}
void f() {
int a = square(3);
Δίνουμε καινούργια
double b = square(3.0);
(μοναδικά) ονόματα σε
}
υπερφορτωμένους
ορισμούς συναρτήσεων…
int square_i(int x) {
return x*x;
}
double square_d(double x) {
return x*x;
}
void f() {
int a = square_i(3); Και στη συνέχεια
double b = square_d(3.0); μετονομάζουμε τις
} κλήσεις (ανάλογα
με τους τύπους των
ορισμάτων τους)
double x;
Coercion στη Java: x = 2;
Παράδειγμα: Java
void f(double x) {
…
}
Η συνάρτηση f μπορεί να κληθεί
f((byte) 1); με κάθε τύπο παραμέτρου που
f((short) 2); μπορεί να μετατραπεί αυτόματα
f('a'); σε double στη Java
f(3);
f(4L);
f(5.6F);
Παράδειγμα: Java
Παραδείγματα… σύγχυσης
Παραμετρικός πολυμορφισμός
Παράδειγμα: Συναρτήσεις σε ML
- fun identity x = x;
val identity = fn : 'a -> 'a
- identity 3;
val it = 3 : int
- identity "hello";
val it = "hello" : string
- fun reverse x =
= if null x then nil
= else (reverse (tl x)) @ [(hd x)];
val reverse = fn : 'a list -> 'a list
Πολυμορφισμός υποτύπων
type
Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
Weekday = Mon..Fri;
function nextDay(D: Day): Day;
begin
if D=Sun then nextDay := Mon else nextDay := D+1
end;
procedure p(D: Day; W: Weekday);
begin
D := nextDay(D);
D := nextDay(W)
end;
Πολυμορφισμός υποτύπων: η
συνάρτηση nextDay μπορεί να
κληθεί με μια παράμετρο υποτύπου
Συμπερασμός Τύπων και Πολυμορφισμός 39
Παράδειγμα: Java
class Car {
void brake() { … }
} Υποτύπος της κλάσης Car
είναι η ManualCar
class ManualCar extends Car
{ Η συνάρτηση g έχει έναν
void clutch() { … } απεριόριστο αριθμό
}
τύπων—ένα για κάθε
void g(Car z) { κλάση που είναι μια
z.brake(); υποκλάση της κλάσης Car
}
void f(Car x, ManualCar y) { Λέμε ότι αυτό είναι
g(x); πολυμορφισμός υποτύπων
g(y);
}
Πολυμορφισμός
• Καθολικός πολυμορφισμός
• Τουλάχιστον όσο το πλήθος των πιθανών τιμών των
μεταβλητών τύπων είναι άπειρο
Πολυμορφισμός υποτύπων
• Καθολικός πολυμορφισμός
• Όσο δεν υπάρχει κάποιο όριο στο πλήθος των
διαφορετικών υποτύπων που μπορεί να δηλωθούν για
κάποιο συγκεκριμένο τύπο
• Συνηθισμένος σε αντικειμενοστρεφείς γλώσσες
προγραμματισμού, όπως η Java
• Συμπερασμός τύπων
– Προσπαθεί να εξάγει τον καλύτερο τύπο για κάθε έκφραση, με
βάση πληροφορία για (κάποια από) τα σύμβολα της έκφρασης
• Πολυμορφισμός
– Όταν κάποια συνάρτηση ή αλγόριθμος μπορεί να δουλέψει σε
πολλούς τύπους δεδομένων
• Υπερφόρτωση (overloading)
– Όταν σύμβολα έχουν πολλαπλές χρήσεις οι οποίες επιλύονται
στο χρόνο μεταγλώττισης (compile time)
• Παραμετρικός πολυμορφισμός
– Single αλγόριθμοι may be given many τύποι
– Type variable may be replaced by any type
– f : t→t => f : int→int, f : bool→bool, ...
• Υπερφόρτωση
– A single symbol may refer to more than one αλγόριθμοι
– Each αλγόριθμοι may have different type
– Η επιλογή του αλγορίθμου determined by type context
– Οι τύποι κάποιου συμβόλου μπορεί να είναι αρκετά διαφορετικοί
– Π.χ. στην ML ο τελεστής + έχει μόνο τους δύο παρακάτω
τύπους:
int * int → int
real * real → real
• ML
- fun swap(x,y) =
= let val z = !x in x := !y; y := z end;
val swap = fn : 'a ref * 'a ref -> unit
• C++
template <typename T>
void swap(T& x, T& y){
T tmp = x; x = y; y = tmp;
}
Υλοποίηση
• ML
– Swap is compiled into one function
– Type checker determines how function can be used
• C++
– Swap is compiled into linkable format
– Linker duplicates code for each type of use
Υπερφόρτωση στην ML
Ανακύκλωση ονομάτων
• Τι ακριβώς συμβαίνει;
Ορισμοί
fun square n = n * n;
fun square square = square * square;
const
Low = 1;
High = 10;
type
Ints = array [Low..High] of Integer;
var
X: Ints;
Εμβέλεια
Μπλοκ (blocks)
let
val x = 1
val y = 2
in
x + y
end
• Ένα let απλώς ορίζει ένα μπλοκ: δεν έχει άλλο σκοπό
• Ένας fun ορισμός περιλαμβάνει ένα μπλοκ:
fun cube x = x * x * x;
while (i < 0) {
int c = i * i;
p += c;
q += c;
i -= step;
}
Παράδειγμα
Δυναμική εμβέλεια
• Μειονεκτήματα:
– Δυσκολία αποδοτικής υλοποίησης
– Δημιουργεί μεγάλες και περίπλοκες εμβέλειες
(οι εμβέλειες επεκτείνονται στις καλούμενες συναρτήσεις)
– Η επιλογή του ονόματος κάποιας μεταβλητής στη συνάρτηση
κλήσης μπορεί να επηρεάσει τη συμπεριφορά της καλούμενης
συνάρτησης
• Πλεονέκτημα:
– Δυνατότητα αλλαγής/επιλογής του περιβάλλοντος εκτέλεσης
program messages;
var message : string;
procedure complain;
begin
writeln(message);
end;
procedure out_of_mem;
var message : string;
begin
message := "Out of memory"; complain;
end;
procedure timeout;
var message : string;
begin
message := "Timeout"; complain;
end;
Ονόματα και Εμβέλεια 36
Αμοιβαία αναδρομή μέσω δηλώσεων forward
Εγγραφές
Δραστηριοποίησης
Ερώτηση για… δέσιμο
Εγγραφές δραστηριοποίησης 2
Εγγραφές δραστηριοποίησης 3
Περιεχόμενα
Εγγραφές δραστηριοποίησης 4
Δραστηριοποιήσεις συναρτήσεων
Εγγραφές δραστηριοποίησης 5
Μεταβλητές ειδικές για κάθε δραστηριοποίηση
Εγγραφές δραστηριοποίησης 6
Εγγραφές δραστηριοποίησης 7
Χρόνοι ζωής για τις μεταβλητές
int count = 0;
int nextcount() {
count = count + 1;
return count;
}
Εγγραφές δραστηριοποίησης 8
Εγγραφές δραστηριοποίησης 9
Άλλοι χρόνοι ζωής για τις μεταβλητές
Εγγραφές δραστηριοποίησης 10
Εγγραφές δραστηριοποίησης
Εγγραφές δραστηριοποίησης 11
Εγγραφές δραστηριοποίησης για μπλοκ
Εγγραφές δραστηριοποίησης 12
Εγγραφές δραστηριοποίησης 13
Παράδειγμα
SUM
AVG
Εγγραφές δραστηριοποίησης 14
Εγγραφές δραστηριοποίησης 15
Στοίβες από εγγραφές δραστηριοποίησης (1)
Εγγραφές δραστηριοποίησης 16
Εγγραφές δραστηριοποίησης 17
Τρέχουσα εγγραφή δραστηριοποίησης
Εγγραφές δραστηριοποίησης 18
int fact(int n) {
Παράδειγμα σε C int result;
if (n < 2) result = 1;
else result = n * fact(n-1);
Αποτιμούμε την κλήση return result;
fact(3). Η εικόνα }
δείχνει τα περιεχόμενα
της στοίβας ακριβώς
πριν την αναδρομική current activation
κλήση της fact(2) record
που θα δημιουργήσει
τη δεύτερη εγγραφή
n: 3
δραστηριοποίησης.
return address
previous
activation record
result: ?
Εγγραφές δραστηριοποίησης 19
int fact(int n) {
Τα περιεχόμενα της int result;
μνήμης ακριβώς πριν if (n < 2) result = 1;
την τρίτη else result = n * fact(n-1);
δραστηριοποίηση. return result;
}
current
activation record
n: 2 n: 3
Εγγραφές δραστηριοποίησης 20
int fact(int n) {
Τα περιεχόμενα της int result;
μνήμης ακριβώς πριν if (n < 2) result = 1;
επιστρέψει η τρίτη else result = n * fact(n-1);
δραστηριοποίηση. return result;
}
current
activation record
n: 1 n: 2 n: 3
Εγγραφές δραστηριοποίησης 21
int fact(int n) {
Η δεύτερη int result;
δραστηριοποίηση if (n < 2) result = 1;
ακριβώς πριν else result = n * fact(n-1);
επιστρέψει. return result;
}
current
activation record
n: 1 n: 2 n: 3
Εγγραφές δραστηριοποίησης 22
int fact(int n) {
Η πρώτη int result;
δραστηριοποίηση if (n < 2) result = 1;
ακριβώς πριν else result = n * fact(n-1);
επιστρέψει με το return result;
αποτέλεσμα }
fact(3) = 6.
current
activation record
n: 1 n: 2 n: 3
Εγγραφές δραστηριοποίησης 23
Οι εικόνες περιλαμβάνουν μια μικρή
“απλοποίηση” όσον αφορά στο τι
Παράδειγμα σε ML πράγματι αποθηκεύεται στη στοίβα
Εγγραφές δραστηριοποίησης 24
current
activation record
Τα περιεχόμενα της
μνήμης ακριβώς
πριν την τρίτη parameter: parameter:
[3,4] [1,2,3,4]
δραστηριοποίηση.
return address return address
previous previous
activation record activation record
a: 3 a: 1
b: 4 b: 2
fun halve nil = (nil, nil)
| halve [a] = ([a], nil) cs: [] cs: [3,4]
| halve (a::b::cs) =
let x: ? x: ?
val (x, y) = halve cs
in y: ? y: ?
(a::x, b::y) value to return: ? value to return: ?
end;
Εγγραφές δραστηριοποίησης 25
Τα περιεχόμενα της
μνήμης ακριβώς πριν
current
activation record
επιστρέψει η τρίτη
δραστηριοποίηση.
Η δεύτερη
δραστηριοποίηση
ακριβώς πριν current
activation record
επιστρέψει.
Εγγραφές δραστηριοποίησης 29
Φωλιασμένες συναρτήσεις
Εγγραφές δραστηριοποίησης 30
Παράδειγμα
Εγγραφές δραστηριοποίησης 31
Το πρόβλημα
Εγγραφές δραστηριοποίησης 32
current
activation record
first caller: a
a split another split quicksort
activation activation activation
quicksort’s
split’s split’s
variables:
variables: variables:
pivot, rest,
x, xs, etc. x, xs, etc.
etc.
Εγγραφές δραστηριοποίησης 33
Σύνδεσμος φωλιάσματος (nesting link)
Εγγραφές δραστηριοποίησης 34
current
activation record
first caller: a
a split another split quicksort
activation activation activation
quicksort’s
split’s split’s
variables:
variables: variables:
pivot, rest,
x, xs, etc. x, xs, etc.
etc.
Εγγραφές δραστηριοποίησης 35
Πως παίρνουν τιμές οι σύνδεσμοι φωλιάσματος;
Εγγραφές δραστηριοποίησης 36
function f1
variable v1
function f2
variable v2
function f3
variable v3
Εγγραφές δραστηριοποίησης 38
Εγγραφές δραστηριοποίησης 39
Συναρτήσεις ως παράμετροι
Εγγραφές δραστηριοποίησης 40
Συναρτήσεις ως παράμετροι
Εγγραφές δραστηριοποίησης 41
Παράδειγμα
fun addX y =
y + x;
in
map addX theList
end;
Εγγραφές δραστηριοποίησης 43
Όχι μόνο για τις παραμέτρους
Εγγραφές δραστηριοποίησης 44
Παράδειγμα
fun addXToAll (x,theList) =
let
curre nt acti vatio n fun addX y =
record y + x;
in
parameter
map addX theList
end;
return address
y => y + x
nesti ng li nk
previo us
activa tion reco rd
Τα περιεχόμενα της μνήμης
x
ακριβώς πριν την κλήση της
theList map. Η μεταβλητή addX είναι
δεμένη με μια συνάρτηση-τιμή η
addX οποία περιλαμβάνει κώδικα και
ένα σύνδεσμο φωλιάσματος.
Εγγραφές δραστηριοποίησης 45
Μακρόβιες εγγραφές δραστηριοποίησης
Εγγραφές δραστηριοποίησης 46
Εγγραφές δραστηριοποίησης 47
val test = current activation
let record
val f = funToAddX 3;
in
f 5 parameter x: 3 return address
end;
return address nesting link: null
fun funToAddX x =
previous
let nesting link: null
activation record
fun addX y =
previous
y + x; f: ?
activation record
in
addX addX
end;
y => y + x
Τα περιεχόμενα της
μνήμης ακριβώς πριν η
funToAddX επιστρέψει.
Εγγραφές δραστηριοποίησης 48
της funToAddX, η f
δεσμεύεται με τη νέα
συνάρτηση-τιμή.
Εγγραφές δραστηριοποίησης 49
Το πρόβλημα
Εγγραφές δραστηριοποίησης 50
• Όσο πιο “σοφιστικέ” είναι μια γλώσσα, τόσο πιο δύσκολο είναι να
δεθούν οι μεταβλητές με θέσεις μνήμης
– Στατική δέσμευση: δουλεύει σε γλώσσες που επιτρέπουν το
πολύ μια δραστηριοποίηση κάθε συνάρτησης κάθε στιγμή (όπως
σε πρώιμες διαλέκτους της Fortran και της Cobol)
– Δυναμική δέσμευση σε στοίβα: δουλεύει σε γλώσσες που δεν
επιτρέπουν φωλιασμένες συναρτήσεις (όπως η C)
– Σύνδεσμοι φωλιάσματος (ή κάτι αντίστοιχο): επιβάλλεται σε
γλώσσες που επιτρέπουν φωλιασμένες συναρτήσεις (όπως η ML,
η Ada και η Pascal). Οι συναρτήσεις τιμές πρέπει να
περιλαμβάνουν τόσο κώδικα όσο και σύνδεσμο φωλιάσματος
– Κάποιες γλώσσες (όπως η ML) επιτρέπουν αναφορές σε
εγγραφές δραστηριοποιήσεων συναρτήσεων που έχουν
περατώσει την εκτέλεσή τους. Κατά συνέπεια, οι εγγραφές
δραστηριοποίησης δε μπορούν να αποδεσμευθούν αμέσως μετά
την επιστροφή τους και η χρήση στοίβας δεν επαρκεί.
Εγγραφές δραστηριοποίησης 52
Εισαγωγή στη
γλώσσα Java
Παράδειγμα αντικειμενοστρεφούς τρόπου σκέψης
Αριθμητικοί τελεστές
Τελεστές σύγκρισης
Τελεστές με παρενέργειες
• Σύνθετες αναθέσεις
String.valueOf(1==2) "false"
String.valueOf(6*7) "42"
String.valueOf(1.0/3.0) "0.3333333333333333"
Εντολές δηλώσεων
<declaration-statement> ::= <declaration> ;
<declaration> ::= <type> <variable-name>
| <type> <variable-name> = <expression>
Η εντολή while
Η εντολή return
/**
* A ConsCell is an element in a linked list of
* ints.
*/
public class ConsCell {
private int head; // the first item in the list
private ConsCell tail; // rest of the list, or null
/**
* Construct a new ConsCell given its head and tail.
* @param h the int contents of this cell
* @param t the next ConsCell in the list, or null
*/
public ConsCell(int h, ConsCell t) {
head = h;
tail = t;
}
/**
* Accessor for the tail of this ConsCell.
* @return the next ConsCell in the list, or null
*/
public ConsCell getTail() {
return tail;
}
}
/**
* Construct a new IntList given its first ConsCell.
* @param s the first ConsCell in the list, or null
*/
public IntList(ConsCell s) {
start = s;
}
/**
* Cons the given element h onto us and return the
* resulting IntList.
* @param h the head int for the new list
* @return the IntList with head h, and us as tail
*/
public IntList cons (int h) {
return new IntList(new ConsCell(h,start));
}
Εισαγωγή στη γλώσσα Java 36
/**
* Get our length.
* @return our int length
*/
public int length() {
int len = 0;
ConsCell cell = start;
while (cell != null) { // while not at end of list
len++;
cell = cell.getTail();
}
return len;
}
}
/**
* Print ourself to System.out.
*/
public void print() {
System.out.print("[");
ConsCell a = start;
while (a != null) {
System.out.print(a.getHead());
a = a.getTail();
if (a != null) System.out.print(",");
}
System.out.println("]");
}
Η μέθοδος main
class Driver {
public static void main(String[] args) {
IntList a = new IntList(null);
IntList b = a.cons(2);
IntList c = b.cons(1);
int x = a.length() + b.length() + c.length();
a.print();
b.print();
c.print();
System.out.println(x);
}
}
Διαχείριση μνήμης 2
Περιεχόμενα
Διαχείριση μνήμης 3
Μοντέλο μνήμης
Διαχείριση μνήμης 4
Δήλωση πινάκων στη Java
Διαχείριση μνήμης 5
Διαχείριση μνήμης 6
Χρησιμοποίηση πινάκων στη Java
int i = 0;
while (i < a.length) {
a[i] = 42;
i++;
}
/**
* MemoryManager constructor.
* @param initialMemory int[] of memory to manage
*/
public MemoryManager(int[] initialMemory) {
memory = initialMemory;
}
…
}
Διαχείριση μνήμης 8
Στοίβες (stacks)
Διαχείριση μνήμης 9
Διαχείριση μνήμης 10
Εικονογράφηση μιας στοίβας
top: 8
Μια κενή στοίβα με 8
7:
λέξεις. Η στοίβα
6: μεγαλώνει προς τα κάτω,
5:
από μεγάλες σε μικρές
διευθύνσεις. Μια
4: προκαθορισμένη θέση
μνήμης (ή πιθανόν ένας
3:
καταχωρητής) αποθηκεύει
2: τη διεύθυνση της
κορυφής της στοίβας
1:
(της μικρότερης
0: χρησιμοποιούμενης
θέσης μνήμης).
Διαχείριση μνήμης 11
top: 4
Διαχείριση μνήμης 12
Το πρόγραμμα τώρα καλεί
την m.push(2), η οποία
top: 1
γυρνάει 2: τη διεύθυνση της
7: πρώτης από τις 2 λέξεις που
6: first activation δεσμεύονται για την
record εγγραφή δραστηριοποίησης.
5: Η στοίβα τώρα γέμισε—δεν
4: 8
υπάρχει χώρος ούτε για μια
κλήση m.push(1).
3:
second Για μια κλήση m.pop(),
2: activation record
αναθέτουμε
1: 4
top = memory[top]
για να επιστρέψουμε στην
0: προηγούμενη κατάσταση.
Διαχείριση μνήμης 13
/**
* StackManager constructor.
* @param initialMemory int[] of memory to manage
*/
public StackManager(int[] initialMemory) {
memory = initialMemory;
top = memory.length;
}
Διαχείριση μνήμης 14
/**
* Allocate a block and return its address.
* @param requestSize int size of block, > 0
* @return block address
* @throws StackOverflowError if out of stack space
*/
public int push(int requestSize) {
int oldtop = top;
top -= (requestSize+1); // extra word for oldtop
if (top < 0) throw new StackOverflowError();
memory[top] = oldtop;
return top+1;
}
/** Pop the top stack frame.
* This works only if the stack is not empty.
*/
public void pop() { Θα δούμε την εντολή throw
top = memory[top];
}
και το χειρισμό εξαιρέσεων
} σε επόμενο μάθημα.
Διαχείριση μνήμης 15
Σωροί (heaps)
Διαχείριση μνήμης 16
Διάταξη σε σωρό
Διαχείριση μνήμης 17
freeStart: 0 0: 10
Διαχείριση μνήμης 19
p1 = m.allocate(4);
9:
8:
7:
Η αναφορά p1 θα πάρει την τιμή 1,
6: -1
δηλαδή τη διεύθυνση της πρώτης από
τις τέσσερις δεσμευμένες λέξεις. 5: 5
freeStart: 5 0: 5
Διαχείριση μνήμης 20
p1 = m.allocate(4);
p2 = m.allocate(2); 9: -1
8: 2
7:
second allocated
Η αναφορά p2 θα πάρει την τιμή 6, block
6:
δηλαδή τη διεύθυνση της πρώτης από
τις δύο δεσμευμένες λέξεις. 5: 3
freeStart: 8 0: 5
Διαχείριση μνήμης 21
p1 = m.allocate(4);
p2 = m.allocate(2); 9: -1
m.deallocate(p1); 8: 2
7:
second allocated
6: block
3:
2:
1: 8
freeStart: 0 0: 5
Διαχείριση μνήμης 22
p1 = m.allocate(4);
p2 = m.allocate(2); 9: -1
m.deallocate(p1); 8: 2
p3 = m.allocate(1);
7:
second allocated
Η αναφορά p3 θα πάρει την τιμή 1, block
6:
δηλαδή τη διεύθυνση της πρώτης
δεσμευμένης λέξης. 5: 3
Διαχείριση μνήμης 23
/**
* HeapManager constructor.
* @param initialMemory int[] of memory to manage
*/
public HeapManager(int[] initialMemory) {
memory = initialMemory;
memory[0] = memory.length; // one big free block
memory[1] = NULL; // free list ends with it
freeStart = 0; // free list starts with it
}
Διαχείριση μνήμης 24
/**
* Allocate a block and return its address.
* @param requestSize int size of block, > 0
* @return block address
* @throws OutOfMemoryError if no block big enough
*/
public int allocate(int requestSize) {
int size = requestSize + 1; // size with header
Διαχείριση μνήμης 27
Ένα πρόβλημα
Διαχείριση μνήμης 29
/**
* Deallocate an allocated block. This works only
* if the block address is one that was returned
* by allocate and has not yet been deallocated.
* @param address int address of the block
*/
public void deallocate(int address) {
int addr = address-1; // real start of the block
int p = freeStart;
int lag = NULL;
while (p != NULL && p < addr) {
lag = p;
p = memory[p+1];
}
Διαχείριση μνήμης 30
// Now p is the index of the block to come after
// ours in the free list, or NULL, and lag is the
// index of the block to come before ours in the
// free list, or NULL.
if (addr+memory[addr] == p) {
memory[addr] += memory[p]; // add its size to ours
p = memory[p+1];
}
Διαχείριση μνήμης 31
Διαχείριση μνήμης 32
Ελεύθερες λίστες διαχωρισμένες βάσει μεγέθους
Διαχείριση μνήμης 33
Κατακερματισμός (fragmentation)
Διαχείριση μνήμης 34
p1 = m.allocate(4);
p2 = m.allocate(1); 9:
m.deallocate(p1); 8: -1
p3 = m.allocate(5);
7: 3
6: second allocated
Η τελευταία δέσμευση θα block
3:
2:
1: 7
freeStart: 0 0: 5
Διαχείριση μνήμης 35
Διαχείριση μνήμης 36
Τρέχοντες σύνδεσμοι στο σωρό
a: start:
IntList a =
b: 2
c: 1 (an IntList) new IntList(null);
int b = 2;
free
(activation record int c = 1;
for main) head: 2 a = a.cons(b);
tail: null a = a.cons(c);
(a ConsCell)
free
head: 1
free tail: Πού είναι οι τρέχοντες
(a ConsCell) σύνδεσμοι στο σωρό
στη διπλανή εικόνα;
free
Στοίβα
the stack Σωρός
the heap
Διαχείριση μνήμης 38
Εύρεση των τρεχόντων συνδέσμων στο σωρό
Διαχείριση μνήμης 40
Δε μπορούμε πάντα να αποφύγουμε τα λάθη
Διαχείριση μνήμης 43
Συλλογή Σκουπιδιών
Διαχείριση μνήμης 44
Κάποια συνηθισμένα προβλήματα με δείκτες
type
p: ^Integer;
begin Ξεκρέμαστος δείκτης: το διπλανό
new(p); πρόγραμμα Pascal χρησιμοποιεί ένα
p^ := 42; δείκτη σε μπλοκ μνήμης και μετά την
dispose(p);
p^ := p^ + 1 αποδέσμευση του συγκεκριμένου μπλοκ
end
procedure Leak;
type Διαρροή μνήμης: το διπλανό πρόγραμμα
p: ^Integer; Pascal δεσμεύει ένα μπλοκ μνήμης αλλά
begin
ξεχνάει να το αποδεσμεύσει
new(p)
end;
Διαχείριση μνήμης 45
Διαχείριση μνήμης 46
Τρεις βασικοί μηχανισμοί ανακύκλωσης μνήμης
Διαχείριση μνήμης 47
Διαχείριση μνήμης 48
Συλλογή σκουπιδιών μέσω αντιγραφής
Διαχείριση μνήμης 49
Μέτρημα αναφορών
Διαχείριση μνήμης 50
Πρόβλημα μετρήματος αναφορών
circle: 2
link:
Διαχείριση μνήμης 51
circle: null 1
link:
Διαχείριση μνήμης 52
Μέτρημα αναφορών
Διαχείριση μνήμης 53
Διαχείριση μνήμης 54
Γλώσσες με αυτόματη διαχείριση μνήμης
Διαχείριση μνήμης 55
Διαχείριση μνήμης 56
Συμπερασματικά
Διαχείριση μνήμης 57
Περισσότερη Java
Πολυμορφισμός υποτύπων
Person p;
• Είναι το παραπάνω μια δήλωση ότι το p είναι μια αναφορά
σε ένα αντικείμενο της κλάσης Person;
• Όχι ακριβώς—ο τύπος Person μπορεί να περιλαμβάνει
αναφορές σε αντικείμενα άλλων κλάσεων
• Αυτό διότι η Java υποστηρίζει πολυμορφισμό υποτύπων
(subtype polymorphism)
Περισσότερη Java 2
Περιεχόμενα
Περισσότερη Java 3
Διαπροσωπείες (interfaces)
Παραδείγματα
Περισσότερη Java 5
Γιατί χρησιμοποιούμε διαπροσωπείες;
Περισσότερη Java 6
Πολυμορφισμός με διαπροσωπείες
static void flashoff(Drawable d, int k) {
for (int i = 0; i < k; i++) {
d.show(0,0);
d.hide();
}
}
Περισσότερη Java 8
/**
* Test whether there are more elements in the
* worklist: that is, test whether more elements
* have been added than have been removed.
* @return true iff there are more elements
*/
boolean hasMore();
/**
* Remove one String from the worklist and return it.
* There must be at least one element in the worklist.
* @return the String item removed
*/
String remove();
}
Περισσότερη Java 9
Σχόλια στις διαπροσωπείες
Περισσότερη Java 10
/**
* A Node is an object that holds a String and a link
* to the next Node. It can be used to build linked
* lists of Strings.
*/
public class Node {
private String data; // Each node has a String...
private Node link; // and a link to the next Node
/**
* Node constructor.
* @param theData the String to store in this Node
* @param theLink a link to the next Node
*/
public Node(String theData, Node theLink) {
data = theData;
link = theLink;
}
Περισσότερη Java 11
/**
* Accessor for the String data stored in this Node.
* @return our String item
*/
public String getData() {
return data;
}
/**
* Accessor for the link to the next Node.
* @return the next Node
*/
public Node getLink() {
return link;
}
}
Περισσότερη Java 12
/**
* A Stack is an object that holds a collection of
* Strings.
*/
public class Stack implements Worklist {
private Node top = null; // top Node in the stack
/**
* Push a String on top of this stack.
* @param data the String to add
*/
public void add(String data) {
top = new Node(data,top);
}
Περισσότερη Java 13
/**
* Test whether this stack has more elements.
* @return true if this stack is not empty
*/
public boolean hasMore() {
return (top != null);
}
/**
* Pop the top String from this stack and return it.
* This should be called only if the stack is
* not empty.
* @return the popped String
*/
public String remove() {
Node n = top;
top = n.getLink();
return n.getData();
}
}
Περισσότερη Java 14
Worklist w;
w = new Stack();
w.add("ο Παρασκευάς.");
w.add("βας, ");
w.add("Βας, βας,");
System.out.print(w.remove());
System.out.print(w.remove());
System.out.println(w.remove());
Περισσότερη Java 15
Επέκταση των κλάσεων
Περισσότερη Java 16
Περισσότερος πολυμορφισμός
Περισσότερη Java 17
/**
* A PeekableStack is an object that does everything
* a Stack can do, and can also peek at the top
* element of the stack without popping it off.
*/
public class PeekableStack extends Stack {
/**
* Examine the top element on the stack, without
* popping it off. This should be called only if
* the stack is not empty.
* @return the top String from the stack
*/
public String peek() {
String s = remove();
add(s);
return s;
}
}
Περισσότερη Java 18
Κληρονομικότητα (inheritance)
Περισσότερη Java 19
Stack s1 = new PeekableStack();
PeekableStack s2 = new PeekableStack();
s1.add("drive");
s2.add("cart");
System.out.println(s2.peek());
Περισσότερη Java 20
Ερώτηση
Περισσότερη Java 21
Απάντηση
Αλυσίδες κληρονομικότητας
Περισσότερη Java 23
Η κλάση Object
Περισσότερη Java 24
Περισσότερη Java 25
Παράδειγμα υπερκάλυψης
Ιεραρχίες κληρονομικότητας
Περισσότερη Java 27
Δύο κλάσεις με πολλά κοινά στοιχεία—αλλά
καμία δεν είναι μια απλή επέκταση της άλλης.
public class Label { public class Icon {
private int x, y; private int x, y;
private int width; private int width;
private int height; private int height;
private String text; private Gif image;
public void move public void move
(int newX, int newY) (int newX, int newY)
{ {
x = newX; x = newX;
y = newY; y = newY;
} }
public String getText() public Gif getImage()
{ {
return text; return image;
} }
} }
Περισσότερη Java 28
Περισσότερη Java 30
Περισσότερη Java 32
Περισσότερη Java 33
Απλές περιπτώσεις
Περισσότερη Java 34
Η δύσκολη περίπτωση
• Μια extends δήλωση επηρεάζει όλες τις πληροφορίες:
– Όλες οι διαπροσωπείες της βασικής κλάσης προσθέτονται στο Α
– Όλες οι μέθοδοι που υποχρεούται η βασική κλάση να ορίσει
προσθέτονται στο Β
– Όλες οι μέθοδοι της βασικής κλάσης προσθέτονται στο Γ
– Όλα τα πεδία της βασικής κλάσης προσθέτονται στο Δ
Περισσότερη Java 35
Το προηγούμενο παράδειγμά μας
Περισσότερη Java 36
Περισσότερη Java 37
Τελικές (final) κλάσεις και μέθοδοι
• Παράδειγμα, η κλάση
java.lang.String
Πολλαπλή κληρονομικότητα
Περισσότερη Java 39
Πολλαπλή κληρονομικότητα (multiple inheritance)
MultiFunction
Προβλήματα συγκρούσεων
Περισσότερη Java 41
Το πρόβλημα του διαμαντιού
B C
D
• Εάν η κλάση A ορίζει ένα πεδίο x, τότε τόσο η B όσο και
η C έχουν ένα
• Δηλαδή η κλάση D έχει δύο τέτοια πεδία;
Περισσότερη Java 42
Περισσότερη Java 43
Ζωή χωρίς πολλαπλή κληρονομικότητα
Περισσότερη Java 44
Προώθηση (forwarding)
public class MultiFunction {
private Printer myPrinter;
private Copier myCopier;
private Scanner myScanner;
private Fax myFax;
Περισσότερη Java 45
Παραμετρικότητα μέσω Generics
Περισσότερη Java 46
Περισσότερη Java 47
Ζωή χωρίς γενικές κλάσεις στις Java 1.0-1.4
Περισσότερη Java 48
Άλλο μειονέκτημα
Περισσότερη Java 53
Java 1.0 έναντι Java με Generics
class Stack { class Stack<T> {
void push(Object o) { void push(T a) { ...
... } }
Object pop() { ... } T pop() { ... }
... ...
} }
String s = "Hello"; String s = "Hello";
Stack st = new Stack(); Stack<String> st =
... new Stack<String>();
st.push(s); st.push(s);
... ...
s = (String) st.pop(); s = st.pop();
Περισσότερη Java 54
Περισσότερη Java 56
Περισσότερη Java 57
Παράδειγμα: Πίνακας κατακερματισμού
interface Hashable {
int HashCode();
};
class HashTable <Key extends Hashable, Value> {
void Insert(Key k, Value v) {
int bucket = k.HashCode();
InsertAt(bucket, k, v);
}
…
};
Η έκφραση πρέπει να μην πετάει σφάλμα
κατά τη διαδικασία ελέγχου των τύπων
Χρησιμοποιούμε “Key extends Hashable”
Περισσότερη Java 58
interface Comparable<I> {
boolean lessThan(I);
};
class PriorityQueue<T implements Comparable<T>> {
T queue[]; …
void insert(T t) {
… if (t.lessThan(queue[i]) …
}
T remove() { … }
…
};
Ένα τελευταίο παράδειγμα …
interface LessAndEqual<I> {
boolean lessThan(I);
boolean equal(I);
}
class Relations<C implements LessAndEqual<C>> extends C {
boolean greaterThan(Relations<C> a) {
return a.lessThan(this);
}
boolean greaterEqual(Relations<C> a) {
return greaterThan(a) || equal(a);
}
boolean notEqual(Relations<C> a) { ... }
boolean lessEqual(Relations<C> a) { ... }
...
}
Περισσότερη Java 61
Αντικειμενοστρέφεια
Ορισμοί αντικειμενοστρέφειας
• Αλλά από την άλλη μεριά, για ποιο λόγο να τους ξέρουμε;
Αντικειμενοστρέφεια 2
Περιεχόμενα
Αντικειμενοστρέφεια 3
datatype response =
Data of string
| Object of message -> response;
Αντικειμενοστρέφεια 7
Η κλάση Stack
Αντικειμενοστρέφεια 8
Η επέκταση των μηνυμάτων και των απαντήσεων,
τόσο για node όσο και για stack
datatype message =
IsNull
| Add of string
| HasMore
| Remove
| GetData
| GetLink;
datatype response =
Pred of bool
| Data of string
| Removed of (message -> response) * string
| Object of message -> response;
Αντικειμενοστρέφεια 11
Αντικειμενοστρέφεια 12
Κάποιες σκέψεις
Αντικειμενοστρέφεια 13
Αντικειμενοστρέφεια 14
Μη αντικειμενοστρεφής Stack
Αντικειμενοστρέφεια 16
Μη αντικειμενοστρεφής Worklist
Αντικειμενοστρέφεια 17
Αντικειμενοστρέφεια 19
Πλεονεκτήματα αντικειμενοστρέφειας
Αντικειμενοστρέφεια 20
Κάποιες σκέψεις
Χαρακτηριστικά αντικειμενοστρέφειας
Αντικειμενοστρέφεια 22
Κλάσεις
Αντικειμενοστρέφεια 24
Με κλάσεις:
x = new Stack(); δημιουργία
στιγμιότυπου
x = {
private Node top = null;
public boolean hasMore() {
return (top != null); Χωρίς κλάσεις:
}
public String remove() { δημιουργία
Node n = top; αντικειμένου
top = n.getLink(); από το μηδέν
return n.getData();
}
…
}
x = y.clone();
Χωρίς κλάσεις:
x.top = null; κλωνοποίηση
πρωτοτύπου
Αντικειμενοστρέφεια 25
Πρωτότυπα
Αντικειμενοστρέφεια 26
Χωρίς κλάσεις αλλά με πρωτότυπα
Αντικειμενοστρέφεια 27
Κληρονομικότητα
Αντικειμενοστρέφεια 28
Ερωτήσεις κληρονομικότητας
Αντικειμενοστρέφεια 29
Ερωτήσεις κληρονομικότητας
• Κληρονομούνται οι προδιαγραφές;
– Ως υποχρεώσεις των μεθόδων για υλοποίηση, όπως στη Java
– Ως επιπλέον προδιαγραφές: invariants, όπως στην Eiffel
• Κληρονομούνται οι τύποι;
– Στη Java κληρονομούνται όλοι οι τύποι της βασικής κλάσης
• Υποστηρίζεται υπερκάλυψη, απόκρυψη, κ.λπ.;
– Τι συμβαίνει στη Java, στο περίπου (χωρίς πολλές λεπτομέρειες):
• Οι κατασκευαστές μπορούν να προσπελάσουν τους κατασκευαστές
των βασικών τους κλάσεων (Αυτό γίνεται με χρήση του super που
καλεί τον κατασκευαστή της άμεσης υπερκλάσης.)
• Μια νέα μέθοδος με το ίδιο όνομα και τύπο με την κληρονομημένη
μέθοδο την υπερκαλύπτει (Η υπερκαλυμμένη μέθοδος μπορεί να
κληθεί με χρήση του προσδιοριστή super.)
• Ένα νέο πεδίο ή στατική μέθοδος αποκρύπτει τις κληρονομημένες, οι
οποίες όμως μπορούν να προσπελαστούν με χρήση του super ή με
στατικούς τύπους της βασικής κλάσης
Αντικειμενοστρέφεια 30
Ενθυλάκωση (encapsulation)
Αντικειμενοστρέφεια 31
Αντικειμενοστρέφεια 33
Παράδειγμα σε Java
Αντικειμενοστρέφεια 34
Δυναμική αποστολή (dynamic dispatch)
Αντικειμενοστρέφεια 35
Αντικειμενοστρέφεια 36
Υλοποιήσεις και τύποι
Αντικειμενοστρέφεια 37
Αντικειμενοστρέφεια 38
Συμπερασματικά
Αντικειμενοστρέφεια 39
Περιεχόμενα
Εξαιρέσεις 3
Κάποιες προκαθορισμένες εξαιρέσεις
Εξαιρέσεις 4
Εξαιρέσεις 5
Ριπτόμενες κλάσεις
Εξαιρέσεις 6
Οι κλάσεις που
παράγονται από την Object
Error χρησιμοποιούνται
Η Java ρίχνει αντικείμενα
για σημαντικά λάθη του
κλάσεων που είναι
συστήματος, όπως π.χ. το Throwable
υποκλάσεις της Throwable
OutOfMemoryError,
από τα οποία συνήθως
δεν μπορούμε να Error Exception
ανακάμψουμε
...
RuntimeException ...
Οι κλάσεις που παράγονται
Οι κλάσεις που παράγονται από από την Exception
τη RuntimeException ... χρησιμοποιούνται για
χρησιμοποιούνται για συνήθη συνήθη λάθη τα οποία το
λάθη του συστήματος, όπως πρόγραμμα μπορεί να
π.χ. ArithmeticException θέλει να πιάσει και να
ανακάμψει από αυτά
Εξαιρέσεις 7
Πιάσιμο εξαιρέσεων
Εξαιρέσεις 8
Η εντολή try
Εξαιρέσεις 9
Παράδειγμα
Εξαιρέσεις 10
Παράδειγμα
Εξαιρέσεις 12
System.out.print("1, ");
try {
String s = null;
s.length();
}
catch (NullPointerException e) {
System.out.print("2, ");
}
System.out.println("3");
Εξαιρέσεις 13
Ρίψη εξαίρεσης από κληθείσα μέθοδο
Εξαιρέσεις 14
Παράδειγμα
void f() {
try {
g();
}
catch (ArithmeticException a) {
… // some action
}
}
• Εάν η g ρίξει μια ArithmeticException και δεν την
πιάσει η ίδια, η εξαίρεση θα προωθηθεί στην f
• Γενικά, ένα throw που θα ρίξει μια εξαίρεση και το
catch που θα την πιάσει μπορεί να διαχωρίζονται από
έναν απροσδιόριστο αριθμό δραστηριοποιήσεων μεθόδων
Εξαιρέσεις 15
• Εάν η z ρίξει μια εξαίρεση που z’s activation
δεν πιάνει, η δραστηριοποίηση record
Εξαιρέσεις 17
Πολλαπλά catch
Παράδειγμα
Εξαιρέσεις 19
Επικαλυπτόμενες προτάσεις catch
Παράδειγμα
Εξαιρέσεις 21
Ρίψη εξαιρέσεων
Εξαιρέσεις 22
Η εντολή throw
Εξαιρέσεις 23
Ριπτόμενες εξαιρέσεις ορισμένες από το χρήστη
System.out.print("1, ");
try {
throw new OutOfGas();
}
catch (OutOfGas e) {
System.out.print("2, ");
}
System.out.println("3");
Εξαιρέσεις 24
Εξαιρέσεις 25
Παράδειγμα χρήσης
try {
throw new OutOfGas("You have run out of gas.");
}
catch (OutOfGas e) {
System.out.println(e.getMessage());
}
Εξαιρέσεις 26
Εξαιρέσεις 27
Περισσότερα για τους κατασκευαστές
Εξαιρέσεις 28
try {
throw new OutOfGas("You have run out of gas.", 42);
}
catch (OutOfGas e) {
System.out.println(e.getMessage());
System.out.println("Odometer: " + e.getMiles());
}
Εξαιρέσεις 29
Ελεγχόμενες εξαιρέσεις
Εξαιρέσεις 30
Ελεγχόμενες εξαιρέσεις
void z() {
throw new OutOfGas("You have run out of gas.", 42);
}
Εξαιρέσεις 31
Ελεγχόμενες εξαιρέσεις
Throwable
Error Exception
Μη ...
ελεγχόμενες RuntimeException ...
εξαιρέσεις
...
Εξαιρέσεις 33
Η πρόταση throws
Εξαιρέσεις 34
Εξαιρέσεις 35
Για ποιο λόγο θέλουμε ελεγχόμενες εξαιρέσεις;
Εξαιρέσεις 36
Εξαιρέσεις 37
Χειρισμός σφαλμάτων
Εξαιρέσεις 38
Χειρισμός σφαλμάτων
Εξαιρέσεις 39
Χρήση προϋποθέσεων
Εξαιρέσεις 41
Καθολικός ορισμός
Εξαιρέσεις 42
/**
* Pop the top int from this stack and return it.
* If the stack is empty we return 0 and leave the
* stack empty.
* @return the popped int, or 0 if the stack is empty
*/
public int pop() {
Node n = top;
if (n == null) return 0;
top = n.getLink();
return n.getData();
}
Εξαιρέσεις 43
Μειονεκτήματα των καθολικών ορισμών
Εξαιρέσεις 44
Θανατηφόρα λάθη
Εξαιρέσεις 47
/**
* Pop the top int from this stack and return it.
* This should be called only if the stack is
* not empty. If called when the stack is empty,
* we set the error flag and return an undefined
* value.
* @return the popped int if stack not empty
*/
public int pop() {
Node n = top;
if (n == null) {
error = true;
return 0;
}
top = n.getLink();
return n.getData();
}
Εξαιρέσεις 48
/**
* Return the error flag for this stack. The error
* flag is set true if an empty stack is ever popped.
* It can be reset to false by calling resetError().
* @return the error flag
*/
public boolean getError() {
return error;
}
/**
* Reset the error flag. We set it to false.
*/
public void resetError() {
error = false;
}
Εξαιρέσεις 49
/**
* Pop the two top integers from the stack, divide
* them, and push their integer quotient. There
* should be at least two integers on the stack
* when we are called. If not, we leave the stack
* empty and set the error flag.
*/
public void divide() {
int i = pop();
int j = pop();
if (getError()) return;
push(i/j);
}
Χρήση εξαιρέσεων
Εξαιρέσεις 51
/**
* Pop the top int from this stack and return it.
* @return the popped int
* @exception EmptyStack if stack is empty
*/
public int pop() throws EmptyStack {
Node n = top;
if (n == null) throw new EmptyStack();
top = n.getLink();
return n.getData();
}
/**
* Pop the two top integers from the stack,
* divide them, and push their integer quotient.
* @exception EmptyStack if stack runs out
*/
public void divide() throws EmptyStack {
int i = pop();
int j = pop();
push(i/j); Η καλούσα μέθοδος δεν ελέγχει
για
}
σφάλμα—απλώς προωθεί την εξαίρεση
Εξαιρέσεις 52
Πλεονεκτήματα
Εξαιρέσεις 54
System.out.print("1");
try {
System.out.print("2");
if (true) throw new Exception();
System.out.print("3");
}
catch (Exception e) {
System.out.print("4");
}
finally {
System.out.print("5");
}
Τι τυπώνεται;
System.out.println("6");
Εξαιρέσεις 57
Μέρη της Java που δεν εξετάσαμε (1)
• Εκλεπτύνσεις
– Εσωτερικές κλάσεις που ορίζουν κλάσεις με εμβέλεια: μέσα σε
άλλες κλάσεις, σε μπλοκ, σε εκφράσεις
– Την εντολή assert (Java 1.4)
• Πακέτα (packages)
– Οι κλάσεις ομαδοποιούνται σε πακέτα
– Σε πολλές υλοποιήσεις της Java, όλα τα αρχεία πηγαίου κώδικα
σε κάποιο directory αντιστοιχούν σε ένα πακέτο
– Η προκαθορισμένη προσπέλαση (χωρίς public, private ή
protected) έχει εύρος πακέτου
Εξαιρέσεις 58
• Δομές ταυτοχρονισμού
– Γλωσσικές δομές συγχρονισμού (synchronization constructs) για
πολλαπλά νήματα εκτέλεσης
– Μέρη του API για τη δημιουργία νημάτων
Πέρασμα παραμέτρων
Τυπικές παράμετροι
int plus(int a, int b)
{
return a+b; Σώμα μεθόδου
}
Πραγματικές
int x = plus(1, 2); παράμετροι
Κλήση μεθόδου
Παράμετροι 2
Περιεχόμενα
• Αντιστοιχία παραμέτρων
• Πέρασμα παραμέτρων
– Κλήση κατά τιμή (call by value)
– Κλήση κατ’ αποτέλεσμα (call by result)
– Κλήση κατά τιμή-αποτέλεσμα (call by value-result)
– Κλήση κατ’ αναφορά (call by reference)
– Κλήση με επέκταση μακροεντολών (call by macro expansion)
– Κλήση κατ’ όνομα (call by name)
– Κλήση κατ’ ανάγκη (call by need)
Παράμετροι 3
Αντιστοιχία παραμέτρων
Παράμετροι 4
Ταίριασμα παραμέτρων μέσω ονομάτων
Παράμετροι 5
Παράμετροι 6
Προαιρετικές παράμετροι
Παράμετροι 9
Παράμετροι 10
int plus(int a, int b) {
a += b; Η συνάρτηση f
return a; επιστρέφει 42
}
int f() {
int x = 3;
int y = 4; current
int z = plus(x, y); activation record
return 6*z;
}
a: 3 x: 3
b: 4 y: 4
Παράμετροι 11
Ορατότητα αλλαγών
Παράμετροι 12
void f() {
ConsCell x = new ConsCell(0, null);
alter(3, x);
}
newHead: 3
Όταν η alter c: x:
Παράμετροι 13
void f() {
ConsCell x = new ConsCell(0, null);
alter(3, x);
}
newHead: 3
Παράμετροι 14
Κλήση κατ’ αποτέλεσμα (call by result)
Παράμετροι 15
a: 3 x: 3
b: 4 y: 4
Όταν η plus c: ? z: ?
αρχίζει την re tur n a d d re s s retur n address
εκτέλεσή της previo us previo us
a ctiva tio n re co rd activatio n re co rd
Παράμετροι 16
void plus(int a, int b, by-result int c) {
c = a+b;
}
int f() {
int x = 3;
int y = 4;
int z;
plus(x, y, z);
return 2*x*z;
} c urre nt
a c tiva tio n re c o rd
a: 3 x: 3
b: 4 y: 4
Όταν η plus c: 7 z: ?
είναι έτοιμη να re tur n a d d re ss re tur n a d d re s s
επιστρέψει p re vio us p re v io us
a c tiva tio n re c o rd a ctiva tio n re co rd
Παράμετροι 17
a: 3 x: 3
b: 4 y: 4
Αφότου η plus c: 7 z: 7
επιστρέψει
retur n address retur n address
previo us previo us
activatio n record activatio n record
Παράμετροι 18
Κλήση κατά τιμή-αποτέλεσμα (call by value-result)
Παράμετροι 19
current
activation record
a: 4 x: 3
b: 3 return address
Όταν η plus
previous
αρχίζει την return address
activation record
εκτέλεσή της previous
activation record
Παράμετροι 20
void plus(int a, by-value-result int b) {
b += a;
}
int f() {
int x = 3;
plus(4, x);
return x*x - x;
}
current
activation record
a: 4 x: 3
b: 7 return address
Όταν η plus
previous
είναι έτοιμη return address
activation record
για επιστροφή previous
activation record
Παράμετροι 21
current
activation record
a: 4 x: 7
b: 7 return address
Όταν η plus
previous
επιστρέψει return address
activation record
previous
activation record
Παράμετροι 22
Κλήση κατ’ αναφορά (call by reference)
current
activation record
a: 4 x: 3
b: return address
Όταν η plus previous
αρχίζει την return address
activation record
εκτέλεσή της previous
activation record
Παράμετροι 24
void plus(int a, by-reference int b) {
b += a;
}
void f() {
int x = 3;
plus(4, x);
}
current
activation record
a: 4 x: 7
b: return address
Όταν η plus previous
εκτελέσει την return address
activation record
ανάθεση previous
activation record
Παράμετροι 25
• Όταν δύο εκφράσεις έχουν την ίδια lvalue, τότε λέμε ότι
είναι συνώνυμα (aliases) η μία της άλλης
• Κάποιες προφανείς περιπτώσεις aliasing
ConsCell x = new ConsCell(0,null);
ConsCell y = x;
Παράμετροι 27
Παράδειγμα
Παράμετροι 28
void sigsum(by-reference int n,
by-reference int ans) {
ans = 0;
int i = 1;
while (i <= n) ans += i++;
}
Παράμετροι 30
Επέκταση μακροεντολών στη C
assembly-
source pre-processor expanded compiler
file language
source
file
a = ((b)<(c)?(b):(c))
Παράμετροι 31
Προεπεξεργασία (preprocessing)
Παράμετροι 32
Επανεκτίμηση (repeated evaluation)
κώδικας
μετά την a = ((b++)<(c++)?(b++):(c++))
επέκταση:
Παράμετροι 33
int main() {
κώδικας int temp = 1, b = 2;
μετά την {int temp = temp; temp = b; b = temp;} ;
επέκταση: printf("%d, %d\n", temp, b);
}
Παράμετροι 34
Πιάσιμο ονομάτων (name capture)
Παράμετροι 36
Υλοποίηση της κλήσης κατ’ όνομα
Παράμετροι 37
a: i: 3
Παράμετροι 38
Σύγκριση
Παράμετροι 39
Παράμετροι 40
int f(by-need int a, by-need int b) { Η συνάρτηση g
b = a; επιστρέφει 4
b = a;
return b;
}
current
int g() { activation record
i+1
int i = 3;
return f(i+1, i);
}
i
a: i: 3
Οκνηρία (laziness)
Παράμετροι 42
Θέματα Σχεδιασμού Γλωσσών
Παράμετροι 43
Παράμετροι 44
Γλώσσες χωρίς παρενέργειες
Παράμετροι 45
Γλώσσες με παρενέργειες
Παράμετροι 46
Ada modes
Παράμετροι 47
Παράμετροι 48
Συμπερασματικά
• Σήμερα είδαμε:
– Πώς ταιριάζουμε τυπικές και πραγματικές παραμέτρους
– Επτά διαφορετικές τεχνικές για πέρασμα παραμέτρων
– Ιδέες για το πού βρίσκεται η διαχωριστική γραμμή μεταξύ
ορισμού γλώσσας και λεπτομέρειας υλοποίησης
Παράμετροι 49
• Λογικός προγραμματισμός
• Όροι (terms)
• Χρήση ενός συστήματος Prolog
• Κανόνες (rules)
• Οι δύο όψεις της Prolog
• Τελεστές (operators)
• Λίστες (lists)
• Άρνηση ως αποτυχία (negation as failure)
• Σε τί είναι καλή η Prolog;
Λογικός προγραμματισμός
Άτομα (Atoms)
Ενοποίηση (Unification)
parent(kim, holly).
parent(margaret, kim).
parent(margaret, kent).
parent(esther, margaret).
parent(herbert, margaret).
parent(herbert, jean).
% swipl
Welcome to SWI-Prolog (Multi-threaded, Version 5.2.13)
Copyright (c) 1990-2003 University of Amsterdam.
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. ...
Please visit http://www.swi-prolog.org for details
?-
Το κατηγόρημα consult/1
?- consult(relations).
% relations compiled 0.00 sec, ΧΧ bytes
Yes
?-
?- [relations].
% relations compiled 0.00 sec, ΧΧ bytes
Yes
Εισαγωγή στη γλώσσα Prolog 13
Απλές ερωτήσεις
?- parent(margaret, kent).
Yes
?- parent(fred, pebbles).
No
?-
Η τελεία
?- parent(margaret, kent)
| .
Yes
?-
X = kim
Yes
Πολλαπλές λύσεις
?- parent(margaret, Child).
Child = kim ;
Child = kent ;
No
Parent = margaret
Grandparent = esther ;
Parent = margaret
Grandparent = herbert ;
No
?- parent(esther, Child),
| parent(Child, Grandchild),
| parent(Grandchild, GreatGrandchild).
Child = margaret
Grandchild = kim
GreatGrandchild = holly
Yes
greatgrandparent(GGP,GGC):-
parent(GGP,GP),
parent(GP,P), συνθήκες
parent(P,GGC).
greatgrandparent(GGP,GGC) :-
parent(GGP,GP),
parent(GP,P), parent(P,GGC).
?- greatgrandparent(esther, GreatGrandchild).
GreatGrandchild = holly ;
No
1. parent(kim,holly).
2. parent(margaret,kim).
3. parent(margaret,kent).
4. parent(esther,margaret).
5. parent(herbert,margaret).
6. parent(herbert,jean).
7. greatgrandparent(GGP,GGC) :-
parent(GGP,GP), parent(GP,P), parent(P,GGC).
greatgrandparent(esther,GreatGrandchild)
Clause 7, binding GGP to esther and GGC to GreatGrandChild
parent(esther,GP), parent(GP,P), parent(P,GreatGrandchild)
Clause 4, binding GP to margaret
parent(margaret,P), parent(P,GreatGrandchild)
grandparent(GP,GC) :-
parent(GP,P), parent(P,GC).
greatgrandparent(GGP,GGC) :-
grandparent(GGP,P), parent(P,GGC).
Αναδρομικοί κανόνες
ancestor(X,Y) :- parent(X,Y).
ancestor(X,Y) :-
parent(Z,Y),
ancestor(X,Z).
?- ancestor(kim, holly).
Yes
?- ancestor(A, holly).
A = kim ;
A = margaret ;
A = esther ;
A = herbert ;
No
greatgrandparent(GGP,GGC) :-
parent(GGP,GP),
parent(GP,P),
parent(P,GGC).
∀GGP, GP, P, GGC . parent (GGP, GP ) ∧ parent (GP, P ) ∧ parent (P, GGC )
⇒ greatgrandparent (GGP, GGC )
X = seth ;
No
X = seth ;
No
Εισαγωγή στη γλώσσα Prolog 36
Αριθμητικοί τελεστές
?- +(X,Y) = 1+2*3.
X = 1
Y = 2*3 ;
No
?- 7 = 1+2*3.
No
Παράδειγμα
?- X = .(1,.(2,.(3,[]))).
X = [1, 2, 3] ;
No
?- .(X,Y) = [1,2,3].
X = 1
Y = [2, 3] ;
No
Το κατηγόρημα append/3
Z = [1, 2, 3, 4] ;
No
X = [1, 2] ;
No
?- append(X, Y, [1,2,3]).
X = []
Y = [1, 2, 3] ;
X = [1]
Y = [2, 3] ;
X = [1, 2]
Y = [3] ;
X = [1, 2, 3]
Y = [] ;
No
append([], L, L).
append([H|L1], L2, [H|L3]) :-
append(L1, L2, L3).
Κατηγόρημα Περιγραφή
member(X,Y) Επιτυγχάνει εάν η λίστα Y έχει ως μέλος τον όρο X.
Επιτυγχάνει εάν η λίστα Y περιέχει το X ως μέλος, και
select(X,Y,Z) ο όρος Z είναι ο ίδιος με τη λίστα Y αλλά χωρίς ένα
στιγμιότυπο του όρου X.
nth0(N,L,X) Επιτυχάνει εάν ο N είναι ένας μη αρνητικός ακέραιος,
L μια λίστα, και το X είναι το N-οστό στοιχείο της
λίστας L, αρχίζοντας την αρίθμηση από το 0.
length(L,N) Επιτυγχάνει εάν ο όρος L είναι μια λίστα με μήκος N.
Z = [1, 3] ; Z = [1, 3, 2] ;
Z = [1, 2, 3] ;
No
No
?- select(2, Y, [1,3]).
?- select(2, [Χ,Y], [1]).
Y = [2, 1, 3] ;
X = 2
Y = [1, 2, 3] ; Y = 1 ;
Y = [1, 3, 2] ; X = 1
Y = 2 ;
No
No
Το κατηγόρημα reverse/2
?- reverse([1,2,3,4], Y).
Y = [4, 3, 2, 1] ;
No
reverse([], []).
reverse([Head|Tail], Reversed) :-
reverse(Tail, RevTail),
append(RevTail, [Head], Reversed).
?- reverse(L, [1,2,3,4]).
L = [4, 3, 2, 1] ;
Το κατηγόρημα sort/2
?- sort([2,3,1,4], X).
X = [1, 2, 3, 4] ;
No
?- sort(X, [1,2,3,4]).
ERROR: Arguments are not sufficiently instantiated
Το κατηγόρημα member/2
member(X, [X|_]).
member(X, [_|T]) :-
member(X, T).
?- member(b, [a,b,c]).
Yes
?- member(d, [a,b,c]).
No
?- member(X, [a,b]).
X = a ;
X = b ;
No
Εισαγωγή στη γλώσσα Prolog 55
Προσοχή στις προειδοποιήσεις!
append([], L, L).
append([H|L1], L2, [H|L3]) :-
append(LI, L2, L3).
Το κατηγόρημα \+/1
?- member(1, [1,2,3]).
Yes
?- \+ member(4, [1,2,3]).
Yes
sibling(X, Y) :-
Παράδειγμα parent(P, X),
parent(P, Y),
\+ (X = Y).
sibling(X, Y) :-
\+ (X = Y),
parent(P, X), ?- sibling(X, Y).
parent(P, Y).
X = kim
Y = kent ;
?- sibling(kim, kent).
X = kent
Yes Y = kim ;
?- sibling(kim, kim). X = margaret
Y = jean ;
No
X = jean
?- sibling(X, Y).
Y = margaret ;
No
No
Κινήσεις
Το κατηγόρημα move/2
change(e, w).
change(w, e).
guarded_or_separated(X, X, X).
guarded_or_separated(_, Y, Z) :- Y \= Z.
Λύσεις
solution([e,e,e,e], []).
solution(Config, [Move|Moves]) :-
move(Config, Move, NextConfig),
safe(NextConfig),
solution(NextConfig, Moves).
L = []
N = 0 ;
L = [_]
N = 1 ;
L = [_, _]
N = 2 ;
L = [_, _, _]
N = 3 ;
L = [_, _, _, _]
N = 4
Yes
Εισαγωγή στη γλώσσα Prolog 68
Yes
Περισσότερη Prolog
Περιεχόμενα
• Ενοποίηση
• Μοντέλα εκτέλεσης της Prolog
– Το διαδικαστικό μοντέλο
– Το μοντέλο υλοποίησης
– Το αφηρημένο μοντέλο
• Περισσότερη Prolog
– Κατηγορήματα εισόδου και εξόδου
– Κατηγορήματα διαχείρισης της βάσης της Prolog
– Αριθμητικοί υπολογισμοί και συγκρίσεις στην Prolog
Περισσότερη Prolog 2
Αντικαταστάσεις (Substitutions)
Περισσότερη Prolog 3
Ενοποίηση
Περισσότερη Prolog 4
Πολλαπλοί ενοποιητές
Περισσότερη Prolog 5
Ο πιο γενικός ενοποιητής (ΠΓΕ)
Περισσότερη Prolog 6
• Πέρασμα παραμέτρων
– reverse([1,2,3],X)
• Δέσιμο μεταβλητών
– X = 0
• Κατασκευή δεδομένων
– X = .(1,[2,3])
• Επιλογή δεδομένων
– [1,2,3] = .(X,Y)
Περισσότερη Prolog 7
Έλεγχος εμφάνισης (Occurs check)
Περισσότερη Prolog 8
?- append([], X, [a | X]).
X = [a, a, a, a, a, a, a, a, a|...] ;
No
Περισσότερη Prolog 9
Μοντέλα Εκτέλεσης της Prolog
Περισσότερη Prolog 10
Περισσότερη Prolog 12
Οπισθοδρόμηση (Backtracking)
Ο αρχικός
όρος προς
Το ίδιο συμβαίνει και με την
απόδειξη t
αντικατάσταση σ2 η οποία
προκύπτει από την διαδικασία
απόδειξης του στόχου σ1(q(Y))
Περισσότερη Prolog 14
Περισσότερη Prolog 15
Παράδειγμα επίλυσης
Περισσότερη Prolog 16
function solve(goals)
if goals is empty then succeed()
else for each clause c in the program, in order
if head(c) does not unify with head(goals) then do nothing
else solve(resolution(c, goals))
Περισσότερη Prolog 17
Παράδειγμα
Περισσότερη Prolog 18
Παραδείγματος συνέχεια
Περισσότερη Prolog 19
Το παράδειγμα ολοκληρωμένο
Περισσότερη Prolog 22
Περισσότερη Prolog 23
Το αφηρημένο μοντέλο: Δένδρα απόδειξης
Περισσότερη Prolog 24
Παράδειγμα
solve [p(X)]
Περισσότερη Prolog 25
Απλοποίηση των δένδρων απόδειξης
Περισσότερη Prolog 26
solve [p(X)]
solve [q(Y),r(Y)]
solve []
Περισσότερη Prolog 27
Σημασιολογία της Prolog
Περισσότερη Prolog 28
Yes
?- read(X).
|: hello(world).
X = hello(world) ;
No
• Οποιοσδήποτε όρος της Prolog μπορεί να διαβαστεί
• Υπάρχει επίσης το κατηγόρημα nl/0 το οποίο είναι
ισοδύναμο με την κλήση write('\n')
Περισσότερη Prolog 29
Το κατηγόρημα assert/1
Yes
?- parent(X,Y).
No
?- assert(parent(joe,mary)).
Yes
?- parent(X,Y).
X = joe
Y = mary ;
No
Περισσότερη Prolog 30
Το κατηγόρημα retract/1
Yes
?- retract(parent(joe, mary)).
Yes
?- parent(joe, mary).
No
?- dynamic parent/2.
Yes
?- assert(parent(joe,mary)).
Yes
?- listing(parent/2).
:- dynamic parent/2.
parent(joe, mary).
Yes
Περισσότερη Prolog 32
Μη αποτιμημένοι όροι
Περισσότερη Prolog 33
Αποτίμηση αριθμητικών εκφράσεων
?- X is 1+2*3.
X = 7 ;
No
?- Y = X+2, X = 1.
Y = 1+2
X = 1 ;
No
?- X = 1, Y is X+2.
X = 1
Y = 3 ;
No
?- Y is X+2, X = 1.
Περισσότερη Prolog 35
Αριθμητικές εκφράσεις και συναρτήσεις
Περισσότερη Prolog 36
Περισσότερη Prolog 38
Αριθμητικές συγκρίσεις
No
?- 1 < 2.0.
Yes
?- 1+2 >= 1+3.
No
?- X is 1-3, Y is 0-2.0, X =:= Y.
X = -2
Y = -2.0 ;
No
Περισσότερη Prolog 39
Συγκρίσεις ισότητας στην Prolog
Περισσότερη Prolog 40
Παραδείγματα
mylength1([],0). mylength2([],0).
mylength1([_|T], Len) :- mylength2([_|T], Len) :-
mylength1(T, LenT), mylength2(T, LenT),
Len = LenT + 1. Len is LenT + 1.
?- mylength1([a,b,c],X). ?- mylength2([a,b,c],X).
X = 0+1+1+1 ; X = 3 ;
No No
?- mylength2(X,3).
X = [_, _, _] ;
No
Περισσότερη Prolog 41
Παράδειγμα: sum
sum([], 0).
sum([Head|Tail],Sum) :-
sum(Tail,TailSum),
Sum is Head + TailSum.
X = 6 ;
No
?- sum([1, 2.5, 3], X).
X = 6.5 ;
No
Περισσότερη Prolog 42
Παράδειγμα: gcd
Περισσότερη Prolog 43
Παράδειγμα: χρήση του κατηγορήματος gcd
?- gcd(5,5,X).
X = 5 ;
No
?- gcd(12,21,X).
X = 3 ;
No
?- gcd(91,105,X).
X = 7 ;
No
?- gcd(91,21*5,7).
Yes
?- gcd(91,X,7).
ERROR: Arguments are not sufficiently instantiated
Περισσότερη Prolog 44
Παράδειγμα: factorial
factorial(X,Fact) :-
X =:= 1, Fact is 1.
factorial(X,Fact) :-
X > 1,
NewX is X - 1,
factorial(NewX,NF),
Fact is X * NF.
?- factorial(5,F).
F = 120 ;
No
?- factorial(2*5,F).
F = 3628800 ;
No
?- factorial(-2,F).
No
Περισσότερη Prolog 45
Διάζευξη και if-then-else στην Prolog
Περισσότερη Prolog 46
factorial(X,Fact) :- factorial(X,Fact) :-
X =:= 1, Fact is 1. ( X =:= 1, Fact is 1
factorial(X,Fact) :- ; X > 1,
X > 1, ⇔ NewX is X - 1,
NewX is X - 1, factorial(NewX,NF),
factorial(NewX,NF), Fact is X * NF
Fact is X * NF. ).
⇔
factorial(X,Fact) :-
( X =:= 1 -> Fact is 1
; X > 1,
NewX is X - 1,
factorial(NewX,NF),
Fact is X * NF
).
Περισσότερη Prolog 47
Παράδειγμα χρήσης if-then-else
gcd(X,Y,GCD) :- gcd(X,Y,GCD) :-
X =:= Y, ( X =:= Y ->
GCD is X. GCD is X
gcd(X,Y,GCD) :- ⇔ ; X < Y ->
X < Y, NewY is Y - X,
NewY is Y - X, gcd(X,NewY,GCD)
gcd(X,NewY,GCD). ; X > Y ->
gcd(X,Y,GCD) :- NewX is X - Y,
X > Y, gcd(NewX,Y,GCD)
NewX is X - Y, ).
gcd(NewX,Y,GCD).
Από τον παραπάνω κώδικα
μπορούμε να παραλείψουμε
τον έλεγχο της συνθήκης
X > Y (και φυσικά το ->)
Περισσότερη Prolog 48
Περισσότερη Prolog 49
Δύο Παραδείγματα Προγραμμάτων
Περισσότερη Prolog 50
Περισσότερη Prolog 51
Το πρόβλημα του σακιδίου (Knapsack problem)
Περισσότερη Prolog 52
Περισσότερη Prolog 53
Αναζήτηση
Περισσότερη Prolog 54
Αναπαράσταση
Περισσότερη Prolog 55
Ακολουθίες και υπακολουθίες
/*
subseq(X, Y) succeeds when list X is the same as
list Y, but with zero or more elements omitted.
This can be used with any pattern of instantiations.
*/
subseq([], []).
subseq([Item | RestX], [Item | RestY]) :-
subseq(RestX, RestY).
subseq(X, [_ | RestY]) :-
subseq(X, RestY).
Κάποια παραδείγματα
?- subseq([1,3],[1,2,3,4]).
Yes
?- subseq(X,[1,2,3]).
X = [1, 2, 3] ;
X = [1, 2] ;
Παρατηρείστε ότι το κατηγόρημα
X = [1, 3] ; subseq/2 όχι μόνο μπορεί να
X = [1] ; ελέγξει κατά πόσο μια λίστα είναι
μια υπακολουθία κάποιας άλλης
X = [2, 3] ; αλλά μπορεί επίσης να παραγάγει
X = [2] ; υπακολουθίες, τις οποίες και θα
χρησιμοποιήσουμε για τη λύση του
X = [3] ; προβλήματος του σακιδίου.
X = [] ;
No
Περισσότερη Prolog 57
/*
knapsackDecision(Items, Capacity, Goal, Knapsack)
takes a list Items of food terms, a positive number
Capacity, and a positive number Goal. We unify
Knapsack with a subsequence of Items representing
a knapsack with total calories >= Goal, subject to
the constraint that the total weight is =< Capacity.
*/
knapsackDecision(Items, Capacity, Goal, Knapsack) :-
subseq(Knapsack, Items),
weight(Knapsack, Weight),
Weight =< Capacity,
calories(Knapsack, Calories),
Calories >= Goal.
Περισσότερη Prolog 58
X = [food(pasta,2,4500), food(peanutButter,1,6700)]
Yes
Περισσότερη Prolog 60
Το κατηγόρημα findall/3
• findall(X, Goal, L)
– Βρίσκει όλους τους τρόπους με τους οποίους ο στόχος Goal
ικανοποιείται
– Για τον καθένα από αυτούς, εφαρμόζει στον όρο X την ίδια
αντικατάσταση με την οποία ο στόχος Goal ικανοποιήθηκε
– Ενοποιεί τη λίστα L με τη λίστα όλων αυτών των X
?- findall(42, subseq(_,[1,2]), L).
No
Περισσότερη Prolog 61
Συλλογή των απαντήσεων που θέλουμε
X = _G396
L = [[1, 2], [1], [2], []] ;
No
Περισσότερη Prolog 62
/*
knapsackOptimization(Items,Capacity,Knapsack) takes
a list Items of food items and a positive integer
Capacity. We unify Knapsack with a subsequence of
Items representing a knapsack of maximum total
calories, subject to the constraint that the total
weight is =< Capacity.
*/
knapsackOptimization(Items, Capacity, Knapsack) :-
findall(K, legalKnapsack(Items,Capacity,K), L),
maxCalories(L, Knapsack).
Περισσότερη Prolog 63
/*
legalKnapsack(Items,Capacity,Knapsack) takes a list
Items of food terms and a positive number Capacity.
We unify Knapsack with a subsequence of Items whose
total weight is =< Capacity.
*/
legalKnapsack(Items, Capacity, Knapsack):-
subseq(Knapsack, Items),
weight(Knapsack, W),
W =< Capacity.
Περισσότερη Prolog 64
/*
maxCalories(List, Result) takes a non-empty List of
lists of food terms. We unify Result with an element
from the list that maximizes the total calories.
We use a helper predicate maxC that takes four
parameters: the remaining list of lists of food
terms, the best list of food terms seen so far, its
total calories, and the final result.
*/
maxCalories([First | Rest], Result) :-
calories(First, FirstC),
maxC(Rest, First, FirstC, Result).
maxC([], Sofar, _, Sofar).
maxC([First | Rest], _, MC, Result) :-
calories(First, FirstC),
MC =< FirstC,
maxC(Rest, First, FirstC, Result).
maxC([First | Rest], Sofar, MC, Result) :-
calories(First, FirstC),
MC > FirstC,
maxC(Rest, Sofar, MC, Result).
Περισσότερη Prolog 65
/*
maxCalories(List, Result) takes a non-empty List of
lists of food terms. We unify Result with an element
from the list that maximizes the total calories.
We use a helper predicate maxC that takes four
parameters: the remaining list of lists of food
terms, the best list of food terms seen so far, its
total calories, and the final result.
*/
maxCalories([First | Rest], Result) :-
calories(First, FirstC),
maxC(Rest, First, FirstC, Result).
maxC([], Sofar, _, Sofar).
maxC([First | Rest], Sofar, MC, Result) :-
calories(First, FirstC),
( MC =< FirstC ->
NSofar = First, NMC = FirstC Το ίδιο με χρήση
;
NSofar = Sofar, NMC = MC
if-then-else
),
maxC(Rest, NSofar, NMC, Result).
Περισσότερη Prolog 66
?- knapsackOptimization(
| [food(bread,4,9200),
| food(pasta,2,4500),
| food(peanutButter,1,6700),
| food(babyFood,3,6900)],
| 4,
| Knapsack).
Knapsack = [food(peanutButter,1,6700),
food(babyFood,3,6900)] ;
Νο
Περισσότερη Prolog 67
Το πρόβλημα των οκτώ βασιλισσών (8-queens)
Περισσότερη Prolog 68
Αναπαράσταση
Περισσότερη Prolog 69
Παράδειγμα
7 Q
6
5 Q
1 Q
1 2 3 4 5 6 7 8
Περισσότερη Prolog 70
/*
nocheck(L, X/Y) takes a queen X/Y and a list
of queens. It succeeds if and only if the X/Y
queen holds none of the others in check.
*/
nocheck([], _).
nocheck([X1/Y1 | Rest], X/Y) :-
X =\= X1,
Y =\= Y1,
abs(Y1-Y) =\= abs(X1-X),
nocheck(Rest, X/Y).
/*
legal(L) succeeds if L is a legal placement of
queens: all coordinates in range and no queen
in check.
*/
legal([]).
legal([X/Y | Rest]) :-
legal(Rest),
member(X, [1,2,3,4,5,6,7,8]),
member(Y, [1,2,3,4,5,6,7,8]),
Περισσότερη Prolog
nocheck(Rest, X/Y). 71
Επάρκεια
X = [] ;
X = [1/1] ;
X = [1/2] ;
X = [1/3]
Περισσότερη Prolog 72
| legal(X). 6 Q
5 Q
X = [8/4, 7/2, 6/7, 5/3, 4 Q
4/6, 3/8, 2/5, 1/1] 3 Q
2 Q
Yes
1 Q
1 2 3 4 5 6 7 8
Περισσότερη Prolog 73
Υπάρχει δυνατότητα για βελτίωση;
Περισσότερη Prolog 74
legal([]).
legal([X/Y | Rest]) :-
legal(Rest),
% member(X, [1,2,3,4,5,6,7,8]), assume X in range
member(Y, [1,2,3,4,5,6,7,8]),
nocheck(Rest, X/Y).
Περισσότερη Prolog 76
Περισσότερη Prolog 77
Ένα πείραμα
legal([]).
legal([X/Y | Rest]) :-
legal(Rest),
% member(X, [1,2,3,4,5,6,7,8]), assume X in range
1 =< Y, Y =< 8, % was member(Y,[1,2,3,4,5,6,7,8]),
nocheck(Rest, X/Y).
Περισσότερη Prolog 78
legal([]).
legal([X/Y | Rest]) :-
% member(X, [1,2,3,4,5,6,7,8]), assume X in range
member(Y, [1,2,3,4,5,6,7,8]),
nocheck(Rest, X/Y),
legal(Rest). % formerly the first condition
Περισσότερη Prolog 79
Εύρεση μίας μόνο λύσης
Περισσότερη Prolog 80
Περισσότερη Prolog 81
Παράδειγμα: Οπισθοδρόμηση χωρίς και με cut
Περισσότερη Prolog 83
Σύντομη Εισαγωγή στην SML/NJ
Φυλλάδιο σημειώσεων για το 1ο εργαστήριο του μαθήματος
Windows
• http://www.smlnj.org/dist/working/110.69/NOTES/WININSTALL
• Start → Programs → SML of New Jersey
• Start → Run, ”cmd”, ”sml”
Unix
• Debian/Ubuntu: apt-get install smlnj smlnj-doc rlwrap
• Εκτέλεση από terminal με sml ή rlwrap sml
• Εναλλακτικά mlton (www.mlton.org)
val it = () : unit
Ε: Και αν ήθελα να τυπώσω τα τετράγωνα ξεκινώντας από το 0;
Α: Καλή ερώτηση. Αυτό μπορεί να γίνει με δύο τρόπους. Ο ένας είναι να χρησιμοποιήσεις
μια επιπλέον μεταβλητή για να αρχίσεις την επανάληψη από το μηδέν και μία για να
ξέρεις που πρέπει να σταματήσεις.
- fun forup n lim =
= if n > lim then ()
= else ( print ( Int . toString ( n * n ) ) ; print " \ n " ; forup ( n +1) lim ) ;
val forup = fn : int -> int -> unit
- forup 0 4;
0
1
4
9
16
val it = () : unit
Ε: Και ο άλλος τρόπος;
2
Α: Ο δεύτερος τρόπος είναι να χρησιμοποιήσεις το σχήμα της αρχικής συνάρτησης fordown
αλλά με μια δομή δεδομένων (π.χ. μια λίστα) ως accumulator όπου θα βάζεις τα
τετράγωνα των αριθμών εκεί, όπως αυτά παράγονται.
- fun forlst n acc =
= if n < 0 then acc
= else forlst (n -1) (( n * n ) :: acc ) ;
val forlst = fn : int -> int list -> int list
- forlst 4 [];
fun len [] = 0
| len ( h :: t ) = 1 + len t
end
Ε: Πως θα σώσω και θα φορτώσω στον interpreter τον κώδικά μου;
Α: Γράφουμε με τον editor της επιλογής μας τον κώδικά μας και τον σώζουμε σε ένα
αρχείο. Στη συνέχεια, μέσα στον interpreter χρησιμοποιούμε τη συνάρτηση use που
παίρνει ως όρισμα ένα string με το όνομα του αρχείου που θέλουμε να φορτώσουμε.
- use " myfile . sml " ;
[ opening myfile . sml ]
val make_list = fn : int -> int list
val len = fn : ’a list -> int
val len2 = fn : ’a list -> int
val it = () : unit
- len [1 ,2 ,3];
val it = 3 : int
Ε: Και αν το αρχείο είναι σε άλλο directory;
Α: Σε αυτήν την περίπτωση, είτε δίνουμε το απόλυτο όνομα του αρχείου, είτε χρησιμο-
ποιούμε τις συναρτήσεις OS.FileSys.getDir() και OS.FileSys.chDir("path"), για να
δούμε που βρισκόμαστε και να περιηγηθούμε στους καταλόγους του συστήματος.
Ε: Και αν βαριέμαι να γράφω use ... κάθε φορά που ξεκινάω την sml;
Α: Mπορείς να φορτώσεις κατ’ ευθείαν τον κώδικα στην SML κατά τη στιγμή της εκκί-
νησης καλώντας την (στο Linux) με την εντολή: sml myfile.sml
3
3 Ταίριασμα προτύπων (pattern matching)
Μπορούμε να κάνουμε pattern matching κατευθείαν στις παραμέτρους της συνάρτη-
σης, όπως φαίνεται στις παρακάτω συναρτήσεις:
fun factorial 0 = 1
| factorial n = n * factorial ( n - 1)
fun length [] = 0
| length ( x :: xs ) = 1+ length xs
Επίσης, μπορούμε να κάνουμε pattern matching σε μεταβλητές με χρήση του case:
fun factorial n =
case n of 0 = > 1
| m = > m * factorial (m -1)
fun length l =
case l of [] => 0
| x :: xs = > 1+ length xs
Σε κάθε pattern matching που κάνουμε πρέπει, στο μέτρο του δυνατού, να φροντίζουμε
να μην εμφανίζεται το μήνυμα Warning: match nonexhaustive, όπως συμβαίνει παρακάτω:
- fun hd [ a ] = a
= | hd ( h :: t ) = h ;
stdIn :1.5 -4.18 Warning : match nonexhaustive
a :: nil = > ...
h :: t = > ...
val it = 20 : int
Για την επιλογή ενός στοιχείου από μια πλειάδα με βάση τη θέση του, η SML παρέχει
έναν ειδικό τελεστή:
- #1 (10 ,20) ;
val it = 10 : int
4
4 Υπερφόρτωση τελεστών
Μερικοί τελεστές της SML είναι υπερφορτωμένοι. Επιτελούν, δηλαδή, διαφορετική
λειτουργία ανάλογα με τον τύπο των τελουμένων τους. Ένας τέτοιος τελεστής είναι ο
γνωστός μας τελεστής + της πρόσθεσης:
- 1 + 2;
val it = 3 : int
- 1.0 + 2.0;
1.0 + 2
Για όσους δεν κατάλαβαν τις ακριβείς συνέπειες του παραπάνω για το τι επιτρέπεται και
τι δεν επιτρέπεται στην ML, δείχνουμε άλλο ένα παράδειγμα:1
- val x : Int64 . int = 1234567890123;
val x = 1234567890123 : Int64 . int
- val y = 42;
val y = 42 : int
- x + y;
stdIn :4.1 -4.7 Error : operator and operand don ’ t agree [ tycon mismatch ]
operator domain : Int64 . int * Int64 . int
operand : Int64 . int * int
in expression :
x + 42
Κατά συνέπεια, πολλές φορές μπορεί να χρειαστεί να χρησιμοποιήσουμε κάποιες από
τις συναρτήσεις μετατροπής τύπων της βιβλιοθήκης:
- val x : Int64 . int = 1234567890123;
val x = 1234567890123 : Int64 . int
- val y = 42;
val y = 42 : int
- x + ( Int64 . fromInt y ) ;
val it = 1234567890165 : Int64 . int
- 3.14 * real 42;
5
5 Ορισμός datatypes
Στην SML μπορούμε να ορίσουμε νέους τύπους δεδομένων, π.χ. ένα δυαδικό δέντρο με
μια απλή δήλωση:
datatype btree = Empty | Leaf | Node of btree * btree
Στη συνέχεια, μπορούμε να γράψουμε συναρτήσεις που επεξεργάζονται τους συγκεκρι-
μένους τύπους δεδομένων, όπως η παρακάτω συνάρτηση που υπολογίζει το πλήθος των
φύλλων ενός δυαδικού δέντρου:
- fun cntleaves Empty = 0
= | cntleaves Leaf = 1
= | cntleaves ( Node ( tree1 , tree2 ) ) =
= ( cntleaves tree1 ) + ( cntleaves tree2 ) ;
val cntleaves = fn : btree -> int
- val tree = Node ( Node ( Leaf , Leaf ) , Leaf ) ;
val tree = Node ( Node ( Leaf , Leaf ) , Leaf ) : btree
- cntleaves tree ;
val it = 3 : int
6 If και βραχυκύκλωση
Η αποτίμηση των λογικών τελεστών στην SML γίνεται με την τεχνική της βραχυκύ-
κλωσης. Αυτό σημαίνει ότι η αποτίμηση σταματάει όταν πλέον το αποτέλεσμά της είναι
γνωστό, π.χ.:
if true orelse 2 div 0 = 1 then print " ok \ n " else print " bad \ n " ;
if false andalso 2 div 0 = 1 then print " bad \ n " else print " ok \ n " ;
Στο παραπάνω παράδειγμα αν η αποτίμηση των συνθηκών δε χρησιμοποιούσε την εν λόγω
τεχνική, τότε θα εμφανιζόταν το μήνυμα uncaught exception Div [divide by zero].
x * 3 )
6
Συναρτήσεις με παρενέργειες (π.χ., εκτύπωση με αλλαγή γραμμής)
(* Wrong way of doing this *)
fun println s = print s ; print " \ n " ;
(* Right way *)
fun println s = ( print s ; print " \ n " ) ;
(* Wrong *)
[1 , 2 , " Hello " ]
(* Right *)
(* Right *)
fun len ( hd :: tl ) = 1 + len tl
| len [] = 0
Επίσης, η σειρά των εμφάνισης των προτύπων έχει σημασία. Η ML μας προειδοποιεί
όταν κάποιο πρότυπο δεν ταιριάζει ποτέ.
datatype ’a tree = Empty | Node of ’a * ’a tree * ’a tree
(* Wrong *)
fun is_leaf Empty = false
| is_leaf ( Node (v , l , r ) ) = false
| is_leaf ( Node (v , Empty , Empty ) ) = true
(* Right *)
fun is_leaf Empty = false
| is_leaf ( Node (v , Empty , Empty ) ) = true
7
8 Συχνές ερωτήσεις
Ε: Η ML έχει κάποιες βιβλιοθήκες που μπορώ να χρησιμοποιήσω;
Α: Βεβαίως. Τη στιγμή συγγραφής αυτού του φυλλαδίου περιγράφονται στις ιστοσελίδες:
• http://www.standardml.org/Basis/overview.html
• http://www.standardml.org/Basis/manpages.html
• http://www.smlnj.org/doc/smlnj-lib/Manual/toc.html
Ε: Υπάρχει κάποιος τρόπος στην ML, για να δω όλα τα στοιχεία μιας δομής δεδομένων
(π.χ. μιας λίστας); Όταν η δομή είναι μεγάλη μου βγάζει τελίτσες από κάποιο σημείο
και μετά...
Α: Ο ένας τρόπος είναι να υλοποιήσεις τη δική σου συνάρτηση ωραίου τυπώματος για
τη δομή δεδομένων που θες να δεις (μάλλον το έχεις κάνει ήδη στην προτροπή που
υπάρχει στη σελίδα 3). Ο άλλος τρόπος στο toplevel του SML/NJ interpreter είναι να
αναθέσεις σε μια αναφορά με όνομα Control.Print.printLength την τιμή που θέλεις.
- [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17];
val it = [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,...] : int list
- Control . Print . printLength := 42;
[ autoloading ]
[ library $smlnj / compiler / current . cm is stable ]
... (* many other lines supressed *)
[ autoloading done ]
val it = () : unit
- [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17];
val it = [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17] : int list
Ασκήσεις εξάσκησης
1. Ταίριασμα προτύπων
Γράψτε πρότυπα που να ταιριάζουν μια μεταβλητή x με την τιμή 0 στις παρακάτω
λίστες και πλειάδα.
(αʹ) print 42
(βʹ) fun foo = 42
(γʹ) fun inc x = x + 1
fun f n = inc true
(δʹ) fun divide_by_two x = x / 2
(εʹ) datatype ’a btree = Leaf of ’a | Node of ’a btree * ’a btree
fun preorder Leaf(v) = [v]
| preorder Node(l,r) = preorder l @ preorder r
8
(Ϛʹ) datatype ’a option = NONE | SOME of ’a
fun filter pred l =
let fun filterP (x::r, l) =
case (pred x) of
SOME y => filterP (r, y::l)
| NONE => filterP (r, l)
| filterP ([], l) = rev l
in
filterP (l, [])
end
(ζʹ) let val e = 5 and f = e + 1 in e + f end
(ηʹ) val (x, x) = (42, 42)
(θʹ) fun f (x, y) = x + y + 1
| f (x, y, z) = x + y + z + 2
| f _ = 3
(ιʹ) fun f _ = "nonzero"
| f 0 = "zero"
(ιαʹ) fun h (x::xs) = x + 1
(ιβʹ) val succ = op + 1
3. Διαχωρισμός λίστας
Να γράψετε μία συνάρτηση που να δέχεται μία λίστα και να τη χωρίζει σε δύο υπο-
λίστες εκ των οποίων η πρώτη θα περιέχει το πρώτο ήμισυ και η άλλη το δεύτερο.
(Αν η αρχική λίστα έχει περιττό μήκος, επιλέξτε εσείς σε ποιά λίστα θα καταλήξει
το μεσαίο στοιχείο.)
4. Τέλειοι αριθμοί
Ένας τέλειος αριθμός ισούται με το άθροισμα των παραγόντων του, συμπεριλαμβα-
νομένης της μονάδας (1) αλλά όχι του εαυτού του. Για παράδειγμα, ο 6 είναι ένας
τέλειος αριθμός γιατί 6 = 3 + 2 + 1. Να γράψετε μία συνάρτηση που να επιστρέφει
true αν ένας αριθμός είναι τέλειος και false αν δεν είναι.
5. Περίγραμμα δέντρου
Γράψτε μια συνάρτηση που παίρνει ένα δέντρο σαν όρισμα, και επιστρέφει μια λίστα
με όλα τα στοιχεία του που είναι φύλλα, από αριστερά προς τα δεξιά, για τον εξής
ορισμό της δομής των δέντρων:
datatype ’a tree = Empty | Node of ’a * ’a tree * ’a tree
6. Δυναμοσύνολο
Να ορίσετε μία συνάρτηση στην οποία όταν δίνεται ένα σύνολο με τη μορφή λίστας
να επιστρέφει το σύνολο των υποσυνόλων του σε μορφή λίστας.
7. Ταξινόμηση
Να γράψετε μία συνάρτηση ταξινόμησης για τη μέθοδο Merge Sort χρησιμοποιώντας
τη συνάρτηση halve:
fun halve nil = ( nil , nil )
| halve [ a ] = ([ a ] , nil )
| halve ( a :: b :: cs ) =
let
val (x , y ) = halve cs
in
( a :: x , b :: y )
end
9
8. Οι πύργοι του Hanoi2
Υποθέστε ότι σας δίνονται τρεις ράβδοι και δίσκοι διαφορετικού μεγέθους. Οι δίσκοι
μπορούν να στοιβαχθούν στις ράβδους σχηματίζοντας πύργους. Βρίσκονται αρχικά
στη ράβδο left με φθίνουσα τάξη μεγέθους. Πρέπει να μεταφερθούν στη ράβδο right
ώστε τελικά να βρεθούν τοποθετημένοι με την ίδια σειρά. Αυτό πρέπει να επιτευχθεί
με τους παρακάτω περιορισμούς:
• Σε κάθε βήμα μόνο ένας δίσκος μεταφέρεται από μία ράβδο σε άλλη.
• Ένας δίσκος απαγορεύεται να τοποθετηθεί πάνω από μικρότερο δίσκο.
• Η ράβδος middle μπορεί να χρησιμοποιηθεί για προσωρινή τοποθέτηση των δί-
σκων.
2 Ιστορική σημείωση: Το παιχνίδι ονομάσθηκε «Πύργος του Ανόι» γιατί το σχήμα του πύργου θυμίζει την
αρχιτεκτονική των ναών στις χώρες της Άπω Ανατολής. Το παιχνίδι πρωτοεμφανίστηκε από τον Γάλλο μαθη-
ματικό Édouard Lucas γύρω στα 1883. Η προέλευσή του είναι μάλλον από τις Ινδίες. Υπάρχει για το παιχνίδι ο
παρακάτω θρύλος με τίτλο «Πύργος του Βράχμα»:
Όταν ο Βράχμα δημιούργησε τον κόσμο, έστησε σε ένα ναό στην πόλη Μπενάρες 64 δακτυλίδια
χρυσού άνισου μεγέθους, όλα περασμένα σε μία ράβδο. Oι ιερείς του ναού έπρεπε να δουλεύουν
μέρα και νύχτα ασταμάτητα για να μεταφέρουν τα δακτυλίδια σε μία άλλη ράβδο, χρησιμοποιώντας
μία τρίτη σαν βοηθητική, έτσι ώστε να μην τοποθετήσουν μεγαλύτερο δακτυλίδι πάνω από μικρότερο
και μετακινώντας ένα μόνο δακτυλίδι σε κάθε κίνηση. Ο θρύλος λέει πως πριν προλάβουν οι ιερείς
να μεταφέρουν όλα τα δακτυλίδια στην άλλη ράβδο, ο ναός θα καταρρεύσει μέσα στην σκόνη και ο
κόσμος θα χαθεί μέσα σε τρομακτικό κρότο βροντής.
10
Σύντομη Εισαγωγή στη Java
Φυλλάδιο σημειώσεων για το 2ο εργαστήριο του μαθήματος
Σχέσεις κλάσεων και αρχείων Τα αρχεία Java έχουν μία πολύ ειδική σχέση με τις
κλάσεις που βρίσκονται σε αυτά. Σε γενικές γραμμές, οι κανόνες είναι οι εξής:
1. Κάθε κλάση ορίζεται σε ακριβώς ένα αρχείο.
2. Το πολύ μία public κλάση μπορεί να οριστεί σε ένα αρχείο.
3. Αν μία public κλάση βρίσκεται σε ένα αρχείο, τότε το όνομα της κλάσης και το
όνομα του αρχείου πρέπει να είναι τα ίδια.
Σύγκριση δύο αντικειμένων Ο τελεστής ισότητας == χρησιμοποιείται για τη σύγκριση
δύο αναφορών σε αντικείμενα. Ελέγχει δηλαδή αν δύο αναφορές δείχνουν στο ίδιο
αντικείμενο. Για τη σύγκριση των ίδιων των αντικειμένων πρέπει να χρησιμοποιηθεί
η μέθοδος equals η οποία κληρονομείται σε όλες τις κλάσεις από την java.lang.Object.
Κατά συνέπεια κληρονομείται και στα strings καθότι και τα strings στη Java είναι
αντικείμενα της κλάσης java.lang.String!
✞ ☎
// Wrong way to find out if the first argument is " - a "
if ( args [0] == " -a " )
optionsAll = true ;
// Right way
if ( " -a " . equals ( args [0]) )
optionsAll = true ;
✝ ✆
1
Αρχικοποίηση πινάκων αντικειμένων Στη Java ένας πίνακας που δηλώνεται ότι εί-
ναι πίνακας σε αντικείμενα κάποιου τύπου, στην πραγματικότητα είναι ένας πίνα-
κας αναφορών σε αντικείμενα αυτού του τύπου. Έτσι, όλα τα στοιχεία του πίνακα
αρχικοποιούνται αυτόματα σε null. Είναι, λοιπόν, απαραίτητο να θέσουμε κάθε στοι-
χείο του πίνακα σε μία πραγματική αναφορά σε κάποιο αντικείμενο του σωστού τύ-
που.
✞ ☎
// Wrong way to create an array of data buffers
StringBuffer [] myTempBuffers ;
myTempBuffers = new StringBuffer [3];
myTempBuffers [0]. add ( data ) ;
✝ ✆
✞ ☎
// Right way to create an array of data buffers and initialize it
StringBuffer [] myTempBuffers ;
myTempBuffers = new StringBuffer [3];
for ( int ix = 0; ix < myTempBuffers . length ; ix ++)
myTempBuffers [ ix ] = new StringBuffer () ;
myTempBuffers [0]. add ( data ) ;
✝ ✆
2
✞ ☎
// A better way to fix the problem
public class ... {
int x = 0; int y = 0; int z = 0;
public boolean hits ( Point [] p2list ) {
for ( int i = 0; i < p2list . length ; i ++) {
Point p2 = p2list [ i ];
if ( p2 . x == x && p2 . y == y )
return true ;
}
return false ;
}
}
✝ ✆
Κλήση του κατασκευαστή της υπερκλάσης Όταν μία κλάση επεκτείνει κάποια άλλη,
τότε ο κατασκευαστής της υποκλάσης πρέπει με κάποιο τρόπο να καλεί τον κατα-
σκευαστή της υπερκλάσης. Συνήθως αυτό επιτυγχάνεται βάζοντας μία κλήση στον
κατασκευαστή της υπερκλάσης στην πρώτη γραμμή του κατασκευαστή της υποκλά-
σης, γράφοντας δηλαδή κάτι σαν και το super(arg1, ..., argN). Αν η πρώτη γραμμή
του κατασκευαστή της υποκλάσης δεν είναι κάτι σαν και το παραπάνω, ο compiler
συνήθως εισάγει μία κλήση super() χωρίς ορίσματα. Αυτό όμως προκαλεί προβλή-
ματα αν η υπερκλάση δεν διαθέτει έναν κατασκευαστή που να μην δέχεται παραμέ-
τρους.
✞ ☎
// Wrong
public class JavaClassFile extends File {
String classname ;
public JavaClassFile ( String cl ) {
classname = cl ;
}
}
✝ ✆
✞ ☎
// Right
public class JavaClassFile extends File {
String classname ;
public JavaClassFile ( String cl ) {
super ( cl + " . class " ) ;
classname = cl ;
}
}
✝ ✆
Οι static και οι λοιπές Ορίζουμε μία μέθοδο ως static, όπως τη main, για να δηλώσουμε
ότι δεν χρειάζεται να δημιουργήσουμε ένα στιγμιότυπο της κλάσης που την περιέχει
για να την καλέσουμε. Όμως από τη στιγμή που δεν έχει δημιουργηθεί ένα στιγμιό-
τυπο της κλάσης, η main δεν μπορεί να χρησιμοποιήσει πεδία της κλάσης ούτε και
να καλέσει κάποιες από τις μεθόδους της, γιατί δεν υπάρχουν! Για την προσπέλαση
ενός πεδίου της κλάσης, θα πρέπει πρώτα να δημιουργήσουμε ένα στιγμιότυπό της.
✞ ☎
// Wrong
public class StaticDemo {
public String my_member_variable = " somedata " ;
public static void main ( String [] args ) {
// Access a non - static member from static method
System . out . println ( " This generates a compiler error " +
my_member_variable ) ;
}
}
✝ ✆
3
✞ ☎
// Right
public class NonStaticDemo {
public String my_member_variable = " somedata " ;
public static void main ( String [] args ) {
NonStaticDemo demo = new NonStaticDemo () ;
// Access member variable of demo
System . out . println ( " This WON ’T generate an error " +
demo . my_member_variable ) ;
}
}
✝ ✆
Όσον αφορά στις μεθόδους μπορούμε είτε να κάνουμε ό,τι κάναμε και για τα πεδία
είτε να ορίσουμε και τη μέθοδο που θέλουμε να καλέσουμε ως static. Αν η εν λόγω
μέθοδος καλεί και άλλες μεθόδους της κλάσης ή χρησιμοποιεί κάποια από τα πεδία
της, τότε ο πρώτος τρόπος συμφέρει περισσότερο.
✞ ☎
// Wrong
public class DivTest {
boolean divisible ( int x , int y ) {
return ( x % y == 0) ;
}
public static void main ( String [] args ) {
int v1 = 14;
int v2 = 9;
// next line causes a compiler error message
if ( divisible ( v1 , v2 ) )
System . out . println ( v1 + " is a multiple of " + v2 ) ;
else
System . out . println ( v2 + " does not divide " + v1 ) ;
}
}
✝ ✆
✞ ☎
// One way to fix the problem
public class DivTest {
int modulus ;
public DivTest ( int m ) { modulus = m ; }
boolean divisible ( int x ) {
return ( x % modulus == 0) ;
}
public static void main ( String [] args ) {
int v1 = 14;
int v2 = 9;
DivTest tester = new DivTest ( v2 ) ;
if ( tester . divisible ( v1 )
System . out . println ( v1 + " is a multiple of " + v2 ) ;
else
System . out . println ( v2 + " does not divide " + v1 ) ;
}
}
✝ ✆
✞ ☎
// Another way to fix the problem
public class DivTest {
static boolean divisible ( int x , int y ) {
return ( x % y == 0) ;
}
public static void main ( String [] args ) {
4
int v1 = 14;
int v2 = 9;
if ( divisible ( v1 , v2 ) )
System . out . println ( v1 + " is a multiple of " + v2 ) ;
else
System . out . println ( v2 + " does not divide " + v1 ) ;
}
}
✝ ✆
Oh strings! Τα strings στην Java είναι αντικείμενα που δεν επιδέχονται μετατροπή (immutable).
Αυτό σημαίνει ότι δεν τα μεταχειριζόμαστε ως πίνακες χαρακτήρων που περνούν
στις μεθόδους κατ’ αναφορά!
✞ ☎
// Wrong
public static void main ( String [] args ) {
String test1 = " Today is " ;
appendTodaysDate ( test1 ) ;
System . out . println ( test1 ) ;
}
public void appendTodaysDate ( String line ) {
line = line + ( new Date () ) . toString () ;
}
✝ ✆
✞ ☎
// One way to fix the problem
public static void main ( String [] args ) {
5
String test1 = " Today is " ;
test1 = appendTodaysDate ( test1 ) ;
System . out . println ( test1 ) ;
}
public String appendTodaysDate ( String line ) {
return ( line + ( new Date () ) . toString () ) ;
}
✝ ✆
✞ ☎
// Another way to fix the problem
public static void main ( String [] args ) {
StringBuffer test1 = new StringBuffer ( " Today is " ) ;
appendTodaysDate ( test1 ) ;
System . out . println ( test1 . toString () ) ;
}
public void appendTodaysDate ( StringBuffer line ) {
line . append (( new Date () ) . toString () ) ;
}
✝ ✆
Long strings:
✞ ☎
// Wrong
String s = " A very long string which just happens to go over the
end of a line and causes a problem with the compiler " ;
// Right
String s = " A very long string which just happens to go over " +
" the end of a line and causes a problem with the compiler " ;
✝ ✆
Κατά τιμή και κατ’ αναφορά Η Java χρησιμοποιεί πέρασμα παραμέτρων και κατά τιμή
και κατ’ αναφορά. Όταν περνάμε σαν παράμετρο έναν πρωτόγονο τύπο δεδομένων,
όπως char, int, float, double, τον περνάμε κατά τιμή. Αυτό σημαίνει ότι στη μέθοδο
που καλείται περνάει ένα αντίγραφο του τύπου δεδομένων το οποίο μπορεί μεν να
μεταβληθεί αλλά έχει διάρκεια ζωής μέχρι το τέλος της μεθόδου. Αντίθετα, όταν περ-
νάμε σαν παράμετρο ένα αντικείμενο, το περνάμε κατ’ αναφορά, δηλαδή οι αλλαγές
που θα γίνουν σε αυτό από τη μέθοδο που καλείται είναι μόνιμες.
Casting Η Java επιτρέπει στον προγραμματιστή να μεταχειριστεί ένα αντικείμενο μίας
υποκλάσης σαν αντικείμενο της υπερκλάσης της. Αυτό συμβαίνει γιατί το upcasting
στην Java γίνεται αυτόματα. Αντίθετα, το downcasting πρέπει να δηλώνεται ρητά.
✞ ☎
// Wrong
Object arr [] = new Object [10];
arr [0] = " m " ;
arr [1] = new Character ( ’m ’) ;
String arg = args [0];
if ( arr [0]. compareTo ( arg ) < 0)
System . out . println ( arg + " comes before " + arr [0]) ;
✝ ✆
✞ ☎
// Right
Object arr [] = new Object [10];
arr [0] = " m " ;
arr [1] = new Character ( ’m ’) ;
String arg = args [0];
if ( (( String ) arr [0]) . compareTo ( arg ) < 0)
System . out . println ( arg + " comes before " + arr [0]) ;
✝ ✆
6
✞ ☎
// File : Input . java
import java . io .*;
2 FAQ
Ε: Πως διαβάζω από το standard input;
Α: Με 3-4 *Reader κάτι μπορεί να γίνει, όπως φαίνεται και στο Σχήμα 1.
Η Java για να χειρίζεται και να διαβάζει αρχεία χρησιμοποιεί τα I/O Streams που ορί-
ζονται στο java.io package. Ένας εύκολος τρόπος για διάβασμα από αρχείο είναι
χρησιμοποιώντας την κλάση BufferedReader, όπως φαίνεται στο παραπάνω παρά-
δειγμα. Αρχικά, φτιάχνουμε ένα InputStream (FileReader) με το αρχείο εισόδου και
με αυτό δημιουργούμε ένα αντικείμενο κλάσης BufferedReader. Με το BufferedReader
που έχουμε ορίσει μπορούμε να διαβάσουμε ένα-ένα χαρακτήρα (read()) ή ολόκληρη
γραμμή από χαρακτήρες (String readLine()). Ένα χρήσιμο εργαλείο είναι το String[]
split(String), που παίρνει ως παράμετρο ένα String και επιστρέφει έναν πίνακα από
Strings. Η συνάρτηση αυτή χωρίζει το αρχικό String σε επιμέρους με βάση κάποιο
διαχωριστικό όπως το κενό, το κόμμα, την παύλα, κ.λπ.
Για παράδειγμα το παρακάτω κομμάτι κώδικα:
String a = "boo:and:foo";
String b = a.split(":");
String c = a.split("o");
7
✞ ☎
// File : Input . java
import java . io .*;
τα χαρακτηριστικά ενός γράφου (πλήθος κόμβων και ακμών και βάρη ακμών) από το
stdin θα μπορούσε να χρησιμοποιήσει κώδικα παρόμοιο με αυτόν που περιέχεται στο
Σχήμα 3.
8
✞ ☎
// File : Graph . java
import java . util . Scanner ;
9
String element = ( String ) itr . next () ;
System . out . print ( element + " " ) ;
}
System . out . println () ;
Κάτι πιο εύκολο από τους iterators, είναι το for:each loop. Ο παραπάνω κώδικας γράφεται
ως εξής:
✞ ☎
System . out . print ( " Original contents of al : " ) ;
for ( String element : a )
System . out . print ( element + " " ) ;
✝ ✆
Ε: Πώς μπορώ να διατάξω ή να συγκρίνω αντικείμενα μιας κλάσης με βάση κάποιο πεδίο;
Α: Η java υποστηρίζει διάταξη αντικειμένων σε κάποιες βασικές κλάσεις όπως Integer,
String. Αν θέλουμε να διατάξουμε κάποια αντικείμενα μια κλάσης που φτιάξαμε μό-
νοι μας πρέπει να δηλώσουμε ότι η κλάση υλοποιεί τη διαπροσωπεία Comparable και
να ορίσουμε μια μέθοδο compareTo(), η οποία θα καθορίζει με βάση ποιο πεδίο θέ-
λουμε να γίνεται η διάταξη των αντικειμένων μας. Η μέθοδος αυτή συγκρίνει δύο
αντικείμενα της κλάσης: το τρέχον αντικείμενο και ένα άλλο το οποίο περνιέται σαν
όρισμα στη μέθοδο. Η τιμή που επιστρέφει η μέθοδος ορίζει τη φυσική σειρά ταξι-
νόμησης για τα αντικείμενα αυτής της κλάσης: Εάν το τρέχον αντικείμενο πρέπει
να ταξινομηθεί πριν (πιο πάνω) από το άλλο αντικείμενο, επιστρέφει -1. Εάν το τρέ-
χον αντικείμενο πρέπει να ταξινομηθεί μετά (πιο κάτω) από το άλλο αντικείμενο,
επιστρέφει 1. Εάν τα δύο αντικείμενα είναι ίσα, επιστρέφει 0. Ο κώδικας του συγκε-
κριμένου παραδείγματος βρίσκεται στην Εικόνα 4.
10
✞ ☎
import java . util .*;
Σχήμα 4: Μια κλάση οριζόμενη από το χρήστη η οποία επιτρέπει σύγκριση μεταξύ αντι-
κειμένων κλάσης Item.
11
3 Λίγη θεωρία...
Χρήσιμες Δομές Δεδομένων
Vector Είναι δομή δεδομένων παρόμοια με έναν παραδοσιακό πίνακα, εκτός από το γε-
γονός ότι μπορεί να μεγαλώνει, όταν απαιτείται, για να δέχεται νέα στοιχεία, ενώ
μπορεί επίσης και να μικραίνει. Όπως και στον πίνακα, τα στοιχεία ενός αντικει-
μένου Vector μπορούν να προσπελάζονται μέσω μιας τιμής δείκτη. Το πλεονέκτημα
που παρέχει είναι ότι δε χρειάζεται να προβληματιστούμε σχετικά με το μέγεθος του
αντικειμένου κατά τη στιγμή της δημιουργίας του καθώς μεγαλώνει και μικραίνει
αυτόματα, όταν και όπως απαιτείται.
✞ ☎
// Create Vector
Vector v = new Vector () ;
// Create vector with initial capacity
Vector v = new Vector (25) ;
// Create Vector with initial capacity and step
Vector v = new Vector (25 ,5) ;
// Add and remove elements
v . add ( " orange " ) ;
v . add ( " apple " ) ;
v . add (2 , " lemon " ) ;
v . remove (2) ;
// Get the last element added
String s = ( String ) v . lastElement () ;
// Get elements using a pointer
String s1 = ( String ) v . get (0) ;
✝ ✆
Hashtable To Hashtable είναι μια δομή που αντιστοιχίζει κλειδιά (keys) σε τιμές (values).
Αυτό είναι χρήσιμο όταν θέλουμε να προσπελάσουμε τα δεδομένα μέσω ενός συγκε-
κριμένου κλειδιού και όχι ενός ακέραιου δείκτη. Τα κλειδιά και οι τιμές μπορούν να
είναι οποιουδήποτε τύπου αντικείμενα. Αντικείμενα εισάγονται ως τιμές με κάποιο
αναγνωριστικό κλειδί, με το οποίο μετά μπορούν να καλεστούν ή να αφαιρεθούν από
το Hashtable.
✞ ☎
// Create a hashtable
Hashtable numbers = new Hashtable () ;
// Add elements
numbers . put ( " one " , new Integer (1) ) ;
numbers . put ( " two " , new Integer (2) ) ;
numbers . put ( " three " , new Integer (3) ) ;
// Find an element
Integer n = ( Integer ) numbers . get ( " two " ) ;
if ( n != null ) {
System . out . println ( " two = " + n ) ;
}
✝ ✆
12
Modifiers
Οι modifiers είναι δεσμευμένες λέξεις που προσθέτονται μπροστά σε ορισμούς για να
αλλάξει η σημασία τους.
Έλεγχος Πρόσβασης
Σύμφωνα με το προκαθορισμένο επίπεδο πρόσβασης, μία μέθοδος ή μεταβλητή είναι
ορατή σε οποιαδήποτε άλλη κλάση περιέχεται στο ίδιο πακέτο. Για να αλλάξουμε το επί-
πεδο πρόσβασης χρησιμοποιούμε τους παρακάτω modifiers.
private Η μέθοδος ή η μεταβλητή είναι ορατή μόνο μέσα στην κλάση στην οποία ορίστηκε.
Μια μεταβλητή private μπορεί να χρησιμοποιείται από μεθόδους της ίδιας κλάσης,
αλλά όχι από αντικείμενα οποιωνδήποτε άλλων κλάσεων. Μια μέθοδος private μπο-
ρεί να καλείται από άλλες μεθόδους της κλάσης, αλλά όχι από άλλες κλάσεις.
public Η μέθοδος ή η μεταβλητή είναι ορατή σε όλες τις κλάσεις. Η μέθοδος main() μιας
εφαρμογής πρέπει να είναι public.
protected Η μέθοδος ή η μεταβλητή είναι ορατή μόνο στις υποκλάσεις της κλάσης στην
οποία έχουν οριστεί και στις άλλες κλάσεις του ίδιου πακέτου.
Άλλοι
static Η μέθοδος ή η μεταβλητή είναι της κλάσης, και όχι κάθε στιγμιοτύπου ξεχωρι-
στά. Μπορεί να προσπελαστεί χωρίς να είναι απαραίτητη η δημιουργία καινούριου
αντικειμένου-στιγμιοτύπου και αν αλλάξει, θα επηρεάσει όλα τα αντικείμενα αυτής
της κλάσης.
abstract Αφορά μια κλάση και σημαίνει ότι δεν μπορούν να δημιουργηθούν αντικείμενα
αυτής της κλάσης, αλλά μόνο των υποκλάσεών της. Ουσιαστικά είναι μια κλάση που
περιέχει κάποια χαρακτηριστικά και μεθόδους για μια ομάδα από υποκλάσεις, και
επιτρέπεται να δημιουργήσουμε αντικείμενα μόνο των υποκλάσεών της. Οι abstract
κλάσεις μπορούν να περιέχουν και abstract μεθόδους, οι οποίες είναι υπογραφές
μεθόδων χωρίς υλοποίηση.(Δεν μπορεί να δηλωθεί μια μέθοδος abstract σε μια κλάση
η οποία δεν είναι επίσης abstract).
Δεν απαιτεί installation, απλό τρέξιμο του εκτελέσιμου eclipse. Προυποθέτει όμως ήδη
εγκατεστημένη Java και χρόνο ή καλή σύνδεση για το download γιατί είναι 151 ΜΒ.
13
Ξεκινώντας
• Στην εκκίνηση του Eclipse, επιλέγουμε Workspace, δηλαδή τον φάκελο όπου θα απο-
θηκεύονται τα Project που θα γράφουμε και πατάμε OK.
• Κλείνουμε την οθόνη υποδοχής και μπροστά μας βλέπουμε το interface που θα χρη-
σιμοποιούμε.
Το πρώτο project
Ένα Project στο Eclipse είναι μια ομαδοποίηση των αρχείων που αποτελούν ένα πρό-
γραμμα. Μπορεί να περιέχει .java αρχεία, μεταγλωττισμένα .class αρχεία, .jar αρχεία
και διάφορα άλλα μέρη.
• Επιλέγουμε File → New → Java Project.
• Δίνουμε όνομα στο project (πεδίο Project Name) και επιλέγουμε Finish.
• Mε διπλό κλικ στο project που δημιουργήθηκε, στον Package explorer, βλέπουμε ότι
περιέχει ένα φάκελο src. Με δεξί κλικ στο φάκελο αυτό, επιλέγουμε New → Class για
να δημιουργήσουμε την πρώτη μας κλάση. Με την ίδια μέθοδο μπορούμε αργότερα
να δημιουργήσουμε και άλλες κλάσεις μέσα στο ίδιο Project.
• Στο πεδίο Name δίνουμε όνομα στην κλάση μας (δε χρειάζεται η επέκταση .java) και
προαιρετικά τσεκάρουμε το κουτί με όνομα
για να δημιουργηθεί αυτόματα ένας σκελετός για τη main στην κλάση μας. Επιλέ-
γουμε Finish.
• Στον editor έχει δημιουργηθεί η κλάση και περιέχει (αν το έχουμε επιλέξει) μια άδεια
main μέθοδο. Συμπληρώνουμε τον κώδικα μας. Για παράδειγμα για ένα απλό Hello
World :
✞ ☎
public class HelloWorld {
/* *
* @param args
*/
public static void main ( String [] args ) {
// TODO Auto - generated method stub
}
}
✝ ✆
14
• Με File → Save. (ή Ctrl+S) το πρόγραμμα μεταγλωττίζεται. Είναι ισοδύναμο με την
εντολή javac HelloWorld.java στη γραμμή εντολών. Αν υπάρχει κάποιο error στη με-
ταγλώττιση θα εμφανιστεί στο παράθυρο Problems στο κάτω μέρος της οθόνης, αμέ-
σως μετά το Save.
• Aν δεν υπάρχουν errors τότε,στο HelloWorld.java στον Package Explorer με δεξί κλικ
επιλέγουμε Run as → Java Application.
• Η έξοδος του προγράμματος τυπώνεται στο παράθυρο Console στο κάτω μέρος της
οθόνης.
Για να τρέξουμε το πρόγραμμα που εκτελέσαμε την τελευταία φορά μπορούμε απλά να
πατήσουμε Ctrl+F11.
Χρήσιμα Εργαλεία
Auto-complete Kάθε φορά που γράφουμε κάτι που πιστεύουμε ότι το Eclipse μπορεί να
το “μαντέψει”, αξίζει να δοκιμάζουμε το Ctrl+Space να δούμε αν θα το κάνει. Το Eclipse
βρίσκει πράγματα όπως ονόματα μεθόδων, μεταβλητές σε εμβέλεια, ονόματα κλάσεων ή
ακόμη και να κάνει προτάσεις για μια μεταβλητή που δηλώνουμε.
Για παράδειγμα, αν ορίσουμε ένα String hello και στη συνέχεια πάμε να το εκτυπώ-
σουμε, πληκτρολογώντας απλά το πρώτο γράμμα του String, δηλαδή το h, και Ctrl+Space
θα έχουμε το αποτέλεσμα που φαίνεται στην παρακάτω εικόνα :
Formatter Aν ο κώδικας σας δεν είναι σωστά στοιχισμένος, είναι πολύ απλό να γίνει
αυτόματα, πατώντας απλά Ctrl+Shift+F σε οποιοδήποτε σημείο του κώδικα σας για να
στοιχίσετε όλο το αρχείο κώδικα, ή Ctrl+I για να στοιχίσετε μια συγκεκριμένη γραμμή.
15
Σχήμα 6: Main template example.
Quick Fix Όταν στον κώδικα μας υπάρχει κάποιο compile error, τότε στο εικονίδιο
του αρχείου που έχει το error, αλλά και σε αυτό του project εμφανίζεται ένα κόκκινο Χ.
Μπορούμε να εντοπίσουμε το λάθος στον κώδικα μας με διπλό κλικ στο συγκεκριμένο
πρόβλημα στο παράθυρο Problems (η επιλέγοντας το κόκκινο σημάδι στην στήλη στα δε-
ξιά του editor). Αριστερά από τη γραμμή όπου υπάρχει το error, υπάρχει είτε απλά ένα Χ,
οπότε πρέπει να βρούμε μόνοι τη λύση στο πρόβλημα μας, είτε ένα Χ με μια μικρή λάμπα,
που σημαίνει ότι το πολύ χρήσιμο Quick Fix του Eclipse έχει κάποια ιδέα για το πώς να
μας βοηθήσει να το λύσουμε. Για να δούμε την πρόταση του, πατάμε Ctrl+1 ή κάνουμε
κλίκ στο είκονίδιο αυτό. Τότε, όπως φαίνεται και στην παρακάτω εικόνα, εμφανίζεται
ένα μενού με προτάσεις για τη διόρθωση του λάθους απ’ όπου επιλέγουμε κάποια που μας
ικανοποιεί, εάν υπάρχει.
Προσοχή : Στο Eclipse η μεταγλώττιση γίνεται όταν κάνουμε Save, επομένως για να
αντιληφθεί ότι διορθώσαμε κάποιο πρόβλημα, μετά τις αλλαγές μας επιλέγουμε πάντα
Ctrl+S ή File → Save.
Setters-Getters Στη Java για να προσπελαύνουμε και να αλλάζουμε τις τιμές private
πεδίων κάποιας κλάσης, χρησιμοποιούμε setters και getters. Το eclipse μπορεί να δη-
μιουργήσει αυτόματα τον κώδικα που χρειάζεται για αυτές τις μεθόδους. Πηγαίνουμε
στο Source και επιλέγουμε Generate Getters and Setters. Στο παράθυρο που εμφανίζεται
υπάρχει η λίστα των πεδίων της κλάσης στην οποία βρισκόμαστε. Μπορούμε να επιλέ-
ξουμε ποια πεδία θέλουμε να δημιουργηθεί αυτόματα κώδικας για μέθοδο Set ή/και Get.
Επίσης μπορούμε να επιλέξουμε πού θα μπει ο κώδικας και το πεδίο πρόσβασης που επι-
θυμούμε να έχει (public,protected,default,private).
16