summaryrefslogtreecommitdiffhomepage
path: root/el-gr/ocaml-gr.html.markdown
blob: 9a15b2d3c20523845083bb4c0b181d6cc58e0067 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
---
language: OCaml
filename: learnocaml-gr.ml
contributors:
    - ["Daniil Baturin", "http://baturin.org/"]
translators:
    - ["Chariton Charitonidis", "https://github.com/haritonch"]
lang: el-gr
---

Η OCaml είναι μία strictly evaluated συναρτησιακή γλώσσα με κάποια στοιχεία
προστακτικού προγραμματισμού.

Μαζί με την StandardML και τις διαλέκτους της, ανήκει στην οικογένεια ML γλωσσών.
Η F# είναι επίσης αρκετά επιρρεασμένη από την OCaml.

Ακριβώς όπως η StandardML, η OCaml διαθέτει έναν interpreter, που μπορεί να
χρησιμοποιηθεί διαδραστικά, αλλά και έναν compiler.
Το εκτελέσιμο αρχείο του interpreter κανονικά έχει το όνομα "ocaml" και ο compiler
έχει το όνομα "ocamlopt".
Υπάρχει και ένας bytecode compiler "ocamlc", αλλά δεν υπάρχουν πολλοί λόγοι να το
χρησιμοποιήσει κάποιος.

Είναι ισχυρά και στατικά τυποποιημένη. Παρ'όλα αυτά , δεν χρειάζεται ο
προγραμματιστής να δηλώνει τους τύπους, καθώς συμπερασμός τύπων γίνεται με τον
αλγόριθμο του συστήματος τύπων Hindley-Milner. Αυτό κάνει τις δηλώσεις τύπων μη
αναγκαίες στις περισσότερες περιπτώσεις, αλλά μπορεί να είναι δύσκολο στην αρχή.

Όταν είμαστε στο toplevel της OCaml (read-eval-print-loop), η OCaml τυπώνει τον
τύπο που συμπεραίνει όταν εισάγουμε μια έκφραση.

```
# let inc x = x	+ 1 ;;
val inc : int -> int = <fun>
# let a = 99 ;;
val a : int = 99
```
Για ένα source αρχείο μπορούμε να χρησιμοποιούμε την εντολή
"ocamlc -i /path/to/file.ml" στο terminal για να τυπώσει όλα τα ονόματα και
τους τύπους.

```
$ cat sigtest.ml
let inc x = x + 1
let add x y = x + y

let a = 1

$ ocamlc -i ./sigtest.ml
val inc : int -> int
val add : int -> int -> int
val a : int
```

Σημειώστε ότι τα type signatures των συναρτήσεων με πολλά ορίσματα είναι
γραμμένα σε curried form. Μια συνάρτηση με πολλά ορίσματα μπορεί να
αναπαρασταθεί ως σύνθεση συναρτήσεων με μόνο ένα όρισμα.
Η "f(x,y) = x + y" από το παράδειγμα, όταν εφαρμόζεται στα ορίσματα 2 και 3
είναι ισοδύναμη με την εφαρμογή της "f0(y) = 2 + y" στο 3. Γι' αυτό έχει τύπο
"int -> int -> int".


```ocaml
(*** Comments ***)

(* Τα σχόλια περικλείονται σε (* και *). Μπορούν να είναι και εμφωλευμένα *)

(* Δεν υπάρχει ειδικό σύμβολο για σχόλια μιας γραμμής *)


(*** Μεταβλητές και Συναρτήσεις ***)

(* Οι εκφράσεις διαχωρίζονται από διπλό semicolon, ";;".
   Σε πολλές περιπτώσεις είναι περιττό, αλλά εδώ θα το χρησιμοποιούμε σε
   κάθε έκφραση για ευκολότερο copy-paste στο interpreter shell.
   Το να χρησιμοποιούμε περιττά ;; σε αρχεία κώδικα θεωρείται συνήθως
   κακό στυλιστικά. *)

(* Οι δηλώσεις μεταβλητών και συναρτήσεων χρησιμοποιούν το keyword "let" *)
let x = 10 ;;

(* Η OCaml επιτρέπει χαρακτήρες μονών εισαγωγικών σε identifiers.
   το μονό εισαγωγικό δεν έχει κάποια σημασία σε αυτή την περίπτωση,
   χρησιμοποιείται συνήθως σε περιπτώσεις που σε άλλες γλώσσες χρησιμοποιούμε
   ονόματα όπως "foo_tmp". *)
let foo = 1 ;;
let foo' = foo * 2 ;;

(* Από τη στιγμή που ο compiler της OCaml συμπεραίνει τους τύπους αυτόματα,
   κανονικά δεν χρειάζεται να δηλώνουμε ρητά τον τύπο ορισμάτων. Παρ'όλα αυτά
   μπορούμε να το κάνουμε αν θέλουμε ή χρειάζεται *)
let inc_int (x: int) : int = x + 1 ;;

(* Μία από αυτές τις περιπτώσεις που είναι αναγκαίο να δηλώσουμε ρητά τύπους
   είναι για να λύσουμε την αμφισημία μεταξύ δύο record types που έχουν πεδία με
   όμοια ονόματα. Η εναλλακτική είναι να βάλουμε αυτούς τους τύπους σε modules,
   αλλά και τα δύο αυτά θέματα είναι εκτός του σκοπού αυτού το μαθήματος. *)

(* Πρέπει να δηλώνουμε ότι μία συνάρτηση είναι αναδρομική με "rec". *)
let rec factorial n =
    if n = 0 then 1
    else n * factorial (n-1)
;;

(* H εφαρμογή συναρτήσεων συνήθως δεν χρειάζεται παρενθέσεις γύρω από ορίσματα *)
let fact_5 = factorial 5 ;;

(* ...εκτός αν τα ορίσματα είναι εκφράσεις *)
let fact_4 = factorial (5-1) ;;
let sqr2 = sqr (-2) ;;

(* Κάθε συνάρητση πρέπει να έχει τουλάχιστον ένα όρισμα.
   Από τη στιγμή που κάποιες συναρτήσεις, από τη φύση τους, δεν παίρνουν κάποιο
   όρισμα, υπάρχει ο τύπος "unit" που έχει μόνο μία τιμή,
   την οποία γράφουμε ως "()". *)
let print_hello () = print_endline "hello world" ;;

(* Προσέχετε ότι πρέπει να γράφουμε το "()" ως όρισμα και όταν την καλούμε. *)
print_hello () ;;

(* Το να καλούμε μια συνάρτηση με λιγότερα ορίσματα από όσα δέχεται
   δεν προκαλεί πρόβλημα, απλά παράγει μια νέα συνάρτηση. *)
let make_inc x y = x + y ;; (* make_inc is int -> int -> int *)
let inc_2 = make_inc 2 ;;   (* inc_2 is int -> int *)
inc_2 3 ;; (* Αποτιμάται σε 5 *)

(* Μπορούμε να χρησιμοποιούμε πολλές εκφράσεις στο σώμα μιας συνάρτησης.
   Η αποτίμηση της τελευταίας έκφρασης είναι η τιμή που επιστρέφει η συνάρτηση.
   Όλες οι ενδιάμεσες εκφράσεις πρέπει να είναι τύπου "unit".
   Αυτό είναι ιδιαίτερα χρήσιμο όταν γράφουμε σε προστακτικό στυλ, η απλούστερη
   μορφή αυτού είναι η εισαγωγή ενός debug print. *)
let print_and_return x =
    print_endline (string_of_int x);
    x
;;

(* Ως συναρτησιακή γλώσσα η OCaml δεν έχει "procedures" (διαδικασίες).
   Κάθε συνάρτηση πρέπει να επιστρέφει κάτι. Οπότε, συναρτήσεις που δεν
   επιστρέφουν κάτι και καλούνται μόνο για τις παρενέργειες τους,
   όπως η print_endline, επιστρέφουν τιμή τύπου "unit". *)


(* Οι ορισμοί μπορούν να γίνουν αλυσιδωτά με τη δομή "let ... in".
   Αυτό είναι περίπου το ίδιο με το να αναθέτουμε τιμές σε πολλές μεταβλητές
   πριν τις χρησιμοποιήσουμε σε εκφράσεις σε προστακτικές γλώσσες. *)
let x = 10 in
let y = 20 in
x + y ;;

(* Εναλλακτικά μπορούμε να χρησιμποιούμε τη δομή "let ... and ... in".
   Αυτό είναι εξαιρετικά χρήσιμο για αμοιβαία αποκλειόμενες συναρτήσεις,
   όπυ με "let .. in", ο compiler θα παραπονιόταν για unbound values *)
let rec
  is_even = function
  | 0 -> true
  | n -> is_odd (n-1)
and
  is_odd = function
  | 0 -> false
  | n -> is_even (n-1)
;;

(* Οι ανώνυμες συναρτήσεις χρησιμοποιούν την εξής σύνταξη: *)
let my_lambda = fun x -> x * x ;;

(*** Τελεστές ***)

(* Δεν υπάρχει ιδιαίτερη διάκριση ανάμεσα σε τελεστές και συναρτήσεις.
   Κάθε τελεστής μπορεί να κληθεί ως συνάρτηση. *)

(+) 3 4  (* Same as 3 + 4 *)

(* Υπάρχει ένας αριθμός built-in τελεστών. Ένα ασυνήθιστο χαρακτηριστικό είναι
   ότι η OCaml δεν μπορεί να κάνει έμμεση μετατροπή τύπων
   ανάμεσα σε ακεραίους και floats, επίσης, χρησιμοποιεί διαφορετικούς τελεστές
   για τους floats (αριθμούς κινητής υποδιαστολής) *)
12 + 3 ;; (* Πρόσθεση ακεραίων. *)
12.0 +. 3.0 ;; (* Πρόσθεση κινητής υποδιαστολής. *)

12 / 3 ;; (* Διαίρεση ακεραίων. *)
12.0 /. 3.0 ;; (* Διαίρεση κινητής υποδιαστολής. *)
5 mod 2 ;; (* Υπόλοιπο. *)

(* Το ενός-ορίσματος μείον είναι αξιοσημείωτη εξαίρεση, είναι πολυμορφικό.
   Ωστόσο, έχει καθαρές μορφές ακεραίων και float. *)
- 3 ;; (* Πολυμορφικό, ακέραιοι *)
- 4.5 ;; (* Πολυμορφικό, float *)
~- 3 (* Μόνο για integer *)
~- 3.4 (* Type error *)
~-. 3.4 (* Μόνο για float *)

(* Μπορούμε να ορίζουμε δικούς μας τελεστές ή να ξανα-ορίσουμε υπάρχοντες.
   Σε αντίθεση με την SML ή τη Haskell, μόνο ορισμένα σύμβολα μπορούν να
   χρησιμοποιηθούν για ονόματα τελεστών και το πρώτο σύμβολο ορίζει την
   επιμεριστικότητα και προτεραιότητα πράξεων. *)
let (+) a b = a - b ;; (* και καλή τύχη στον επόμενο... *)

(* Πιο χρήσιμο: ένας τελεστής αντιστρόφου για floats.
   οι τελεστές ενός-ορίσματος πρέπει να ξεκινούν με "~". *)
let (~/) x = 1.0 /. x ;;
~/4.0 (* = 0.25 *)


(*** Built-in δομές δεδομένων ***)

(* Οι λίστες περικλείονται από αγκύλες και τα στοιχεία τους
   διαχωρίζονται με semicolons. *)
let my_list = [1; 2; 3] ;;

(* Οι tuples (προαιρετικά) περικλείονται από παρενθέσεις, τα στοιχεία τους
   διαχωρίζονται με κόμματα. *)
let first_tuple = 3, 4 ;; (* Έχει τύπο "int * int". *)
let second_tuple = (4, 5) ;;

(* Συνέπεια: αν προσπαθήσεουμε να διαχωρίσουμε τα στοιχεία μιας λίστας
   με κόμματα, θα πάρουμε μια λίστα με ένα tuple ως στοιχείο.
   Μπορεί να την πατήσουμε εύκολα έτσι. *)
let bad_list = [1, 2] ;; (* Becomes [(1, 2)] *)

(* Μπρούμε να προσπελάσουμε στοιχεία μιας λίστας με τη συνάρτηση List.nth. *)
List.nth my_list 1 ;;

(* Yπάρχουν συναρτήσεις ανώτερης τάξης για λίστες, όπως οι map και filter. *)
List.map (fun x -> x * 2) [1; 2; 3] ;;
List.filter (fun x -> x mod 2 = 0) [1; 2; 3; 4] ;;

(* Μπορούμε να προσθέτουμε στοιχεία στην αρχή μιας λίστας με τον
   constructor "::", συνήθως αναφέρεται ως "cons". *)
1 :: [2; 3] ;; (* Αποτέλεσμα: [1; 2; 3] *)

(* Οι πίνακες Arrays περικλείονται από [| |] *)
let my_array = [| 1; 2; 3 |] ;;

(* Προσπελαύνουμε στοιχεία ενός πίνακα ως εξής: *)
my_array.(0) ;;


(*** Strings και Χαρακτήρες ***)

(* Χρησιμοποιούμε διπλά εισαγωγικά για τα string literals. *)
let my_str = "Hello world" ;;

(* Μονά εισαγωγικά για τα literals χαρακτήρων. *)
let my_char = 'a' ;;

(* Τα μονά και τα διπλά εισαγωγικά δεν είναι ισοδύναμα. *)
let bad_str = 'syntax error' ;; (* Syntax error. *)

(* Αυτό μας δίνει ένα string με έναν χαρακτήρα και όχι εναν χαρακτήρα. *)
let single_char_str = "w" ;;

(* Τα strings παρατίθενται με τον τελεστή "^". *)
let some_str = "hello" ^ "world" ;;

(* Τα strings δεν είναι πίνακες από χαρακτήρες όπως στην C.
   Δεν μπορούμε να ανακατεύουμε strings με χαρακτήρες σε εκφράσεις.
   Μπορούμε να μετατρέπουμε χαρακτήρες σε strings με "String.make 1 my_char".
   Υπάρχουν πιο βολικές συναρτήσεις για αυτό το σκοπό σε πρόσθετες βιβλιοθήκες,
   όπως η Core.Std που μπορεί να μην έχουν εγκατασταθεί/φορτωθεί by default. *)
let ocaml = (String.make 1 'O') ^ "Caml" ;;

(* Υπάρχει και μια συνάρτηση printf. *)
Printf.printf "%d %s" 99 "bottles of beer" ;;

(* Υπάρχουν και συναρτήσεις read/write χωρίς μορφοποίηση. *)
print_string "hello world\n" ;;
print_endline "hello world" ;;
let line = read_line () ;;


(*** User-defined τύποι δεδομένων ***)

(* Μπορούμε να ορίζουμε τύπους δεδομένων με τη δομή "type some_type".
   Όπως σε αυτό τον άχρηστο τύπο που αντιγράφει τους ακεραίους: *)
type my_int = int ;;

(* Πιο ενδιαφέροντες τύποι περιλαμβάνουν τους λεγόμενους type constructors.
   Αυτοί πρέπει να ξεκινούν με κεφαλαίο γράμμα. *)
type ml = OCaml | StandardML ;;
let lang = OCaml ;;  (* Έχει τύπο "ml". *)

(* Οι type constructors δε χρειάζεται να είναι κενοί. *)
type my_number = PlusInfinity | MinusInfinity | Real of float ;;
let r0 = Real (-3.4) ;; (* Έχει τύπο "my_number". *)

(* Μπορούν να χρησιμοποιηθούν για πολυμορφική αριθμιτική *)
type number = Int of int | Float of float ;;

(* Σημείο στο επίπεδο, βασικά ένα tuple περιορισμένου συγκεκριμένου τύπου *)
type point2d = Point of float * float ;;
let my_point = Point (2.0, 3.0) ;;

(* Οι τύποι μπορούν να είναι παραμετροποιημένοι, όπως σε αυτόν τον τύπο για
   λίστες λίστών με οτιδήποτε τύπου στοιχεία. Το 'a μπορεί να αντικατασταθεί από
   οποιονδήποτε τύπο. *)
type 'a list_of_lists = 'a list list ;;
type int_list_list = int list_of_lists ;;

(* Οι τύποι μπορούν επίσης να ορίζονται αναδρομικά. Σαν αυτόν εδώ τον τύπο που
   είναι ανάλογος της built in λίστας από ακεραίους. *)
type my_int_list = EmptyList | IntList of int * my_int_list ;;
let l = IntList (1, EmptyList) ;;


(*** Ταίριασμα Προτύπων - Pattern Matching ***)

(* Το ταίριασμα προτύπων είναι κάπως σαν το switch statement σε προστακτικές
   γλώσσες προγραμματισμού, αλλά παρέχει πολύ μεγαλύτερη εκφραστική ισχύ.

   Παρόλο που φαίνεται περίπλοκο, στην πραγματικότητα είναι απλώς ταίριασμα
   ενός ορίσματος με μια συγκεκριμένη τιμή, ένα κατηγόρημα ή έναν type constructor
   Το σύστημα τύπων είναι αυτό που το κάνει τόσο ισχυρό. *)

(** Ταίριασμα με ακριβείς τιμές.  **)

let is_zero x =
    match x with
    | 0 -> true
    | _ -> false  (* Το "_" σημαίνει "οτιδήποτε άλλο". *)
;;

(* Εναλλακτικά μπορούμε να χρησιμοποιούμε το keyword "function". *)
let is_one = function
| 1 -> true
| _ -> false
;;

(* Ταίριασμα με κατηγορήματα, γνωστό και ως "guarded pattern matching". *)
let abs x =
    match x with
    | x when x < 0 -> -x
    | _ -> x
;;

abs 5 ;; (* 5 *)
abs (-5) ;; (* 5 πάλι *)

(** Ταίριασμα με type constructors **)

type animal = Dog of string | Cat of string ;;

let say x =
    match x with
    | Dog x -> x ^ " says woof"
    | Cat x -> x ^ " says meow"
;;

say (Cat "Fluffy") ;; (* "Fluffy says meow". *)

(** Διάσχιση δομών δεδομένων με ταίριασμα προτύπων **)

(* Οι αναδρομικοί τύποι μπορούν να διασχιστούν εύκολα με ταίριασμα προτύπων.
   Ας δούμε πώς μπορούμε να διασχίσουμε μια λίστα.
   Παρόλο που το built-in cons ("::") μοιάζει με infix τελεστή,
   στην πραγματικότητα είναι ένας type constructor και μπορεί να
   ταιριαστεί όπως όλοι οι type constructors. *)
let rec sum_list l =
    match l with
    | [] -> 0
    | head :: tail -> head + (sum_list tail)
;;

sum_list [1; 2; 3] ;; (* Αποτιμάται σε 6 *)

(* Η built-in συνταξη των cons εμποδίζει τη δομή λίγο, γι αυτό θα φτιάξουμε
   το δικό μας τύπο λίστας για την παρουσίαση. *)
type int_list = Nil | Cons of int * int_list ;;
let rec sum_int_list l =
  match l with
      | Nil -> 0
      | Cons (head, tail) -> head + (sum_int_list tail)
;;

let t = Cons (1, Cons (2, Cons (3, Nil))) ;;
sum_int_list t ;;
```

## Περισσότερα για την OCaml

* Επισκεφθείτε την επίσημη σελίδα της OCaml για να κατεβάσετε τον compiler και να διαβάσετε το documentation: <http://ocaml.org/>
* Δοκιμάστε διαδραστικά μαθήματα και έναν web-based interpreter από την OCaml Pro: <http://try.ocamlpro.com/>