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
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
|
---
language: Scala
contributors:
- ["George Petrov", "http://github.com/petrovg"]
- ["Dominic Bou-Samra", "http://dbousamra.github.com"]
- ["Geoff Liu", "http://geoffliu.me"]
translators:
- ["Vasilis Panagiotopoulos" , "https://github.com/billpcs/"]
filename: learnscala-gr.scala
lang: el-gr
---
Scala - Η επεκτάσιμη γλώσσα
```scala
/*
Προετοιμαστείτε:
1) Κατεβάστε την Scala - http://www.scala-lang.org/downloads
2) Κάνετε εξαγωγή στην επιθυμητή σας τοποθεσία και βάλτε τον υποφάκελο bin
στο path του συστήματος
3) Ξεκινήστε ένα scala REPL γράφοντας scala. Θα πρέπει να βλέπετε το prompt:
scala>
Αυτό είναι το αποκαλούμενο REPL (Read-Eval-Print Loop) *.
Μπορείτε να πληκτρολογήσετε οποιαδήποτε έγκυρη έκφραση σε Scala μέσα του ,
και το αποτέλεσμα θα τυπωθεί. Θα εξηγήσουμε πως μοιάζουν τα αρχεία της Scala
αργότερα μέσα στο tutorial , αλλά για τώρα ας αρχίσουμε με κάποια βασικά.
*[Βρόχος του Διάβασε - Αξιολόγησε - Τύπωσε]
*/
/////////////////////////////////////////////////
// 1. Βασικές έννοιες
/////////////////////////////////////////////////
// Τα σχόλια μίας γραμμής ξεκινούν με δύο "/" (:forward slashes) .
/*
Τα σχόλια που επεκτείνονται σε πολλές γραμμές , όπως μπορείτε
να δείτε , φαίνονται κάπως έτσι.
*/
// Εκτύπωση με νέα γραμμή στην επόμενη εκτύπωση
println("Hello world!")
println(10)
// Εκτύπωση χωρίς νέα γραμμή στην επόμενη εκτύπωση
print("Hello world")
// Η δήλωση μεταβλητών γίνεται χρησιμοποιώντας var ή val.
// Οι δηλώσεις val είναι αμετάβλητες, ενώ οι var είναι μεταβλητές.
// Η αμεταβλητότητα είναι συμφέρουσα και προσπαθούμε να την χρησιμοποιούμε.
val x = 10 // το x είναι τώρα 10
x = 20 // σφάλμα: αλλαγή σε val
var y = 10
y = 20 // το y είναι τώρα 20
/*
Η Scala είναι στατικού τύπου γλώσσα, εν τούτοις προσέξτε ότι στις παραπάνω
δηλώσεις , δεν προσδιορίσαμε κάποιον τύπο. Αυτό συμβαίνει λόγω ενός
χαρακτηριστικού της Scala που λέγεται συμπερασματολογία τύπων. Στις
περισσότερες των περιπτώσεων, ο μεταγλωττιστής της Scala μπορεί να
μαντέψει ποιος είναι ο τύπος μιας μεταβλητής. Μπορούμε να δηλώσουμε
αναλυτικά τον τύπο μιας μεταβλητής ως εξής:
*/
val z: Int = 10
val a: Double = 1.0
/*
Προσέξτε ότι υπάρχει αυτόματη μετατροπή από ακέραιο (Int) σε διπλής
ακρίβειας (Double), και συνεπώς το αποτέλεσμα είναι 10.0 και όχι 10.
*/
val b: Double = 10
// Λογικές τιμές
true
false
// Λογικές Πράξεις
!true // false
!false // true
true == false // false
10 > 5 // true
// Η αριθμητική είναι όπως τα συνηθισμένα
1 + 1 // 2
2 - 1 // 1
5 * 3 // 15
6 / 2 // 3
6 / 4 // 1
6.0 / 4 // 1.5
/*
Αξιολογώντας μια έκφραση στο REPL, σας δίνεται ο τύπος και
η τιμή του αποτελέσματος
*/
1 + 7
/* Η παραπάνω γραμμή έχει το εξής αποτέλεσμα:
scala> 1 + 7
res29: Int = 8
Αυτό σημαίνει ότι το αποτέλεσμα της αξιολόγησης του 1 + 7 είναι ένα αντικείμενο
τύπου Int με τιμή 8
Σημειώστε ότι το "res29" είναι ένα σειριακά δημιουργούμενο όνομα μεταβλητής
για να αποθηκεύονται τα αποτελέσματα των εκφράσεων που έχετε πληκτρολογήσει
και συνεπώς η έξοδός σας μπορεί να διαφέρει.
*/
"Τα αλφαριθμητικά στην Scala περικλείονται από διπλά εισαγωγικά"
'a' // Ένας χαρακτήρας στην Scala
// res30: Char = a
// Αλφαριθημτικά με μονά εισαγωγικά δεν υφίστανται <= Αυτό θα προκαλέσει σφάλμα.
// Τα αλφαριθμητικά έχουν τις συνηθισμένες μεθόδους της Java ορισμένες πάνω τους.
"hello world".length
"hello world".substring(2, 6)
"hello world".replace("C", "3")
// Έχουν επίσης μερικές επιπλέον μεθόδους Scala.
// Δείτε επίσης : scala.collection.immutable.StringOps
"hello world".take(5)
"hello world".drop(5)
// Παρεμβολή αλφαριθμητικών : παρατηρήστε το πρόθεμα "s"
val n = 45
s"We have $n apples" // => "We have 45 apples"
// Παρατηρήστε την χρήση των '{', '}'
val a = Array(11, 9, 6)
s"My second daughter is ${a(0) - a(2)} years old." // => "My second daughter is 5 years old."
s"We have double the amount of ${n / 2.0} in apples." // => "We have double the amount of 22.5 in apples."
s"Power of 2: ${math.pow(2, 2)}" // => "Power of 2: 4"
// Μορφοποίηση με παρεμβεβλημένα αλφαριθμητικά με το πρόθεμα "f"
f"Power of 5: ${math.pow(5, 2)}%1.0f" // "Power of 5: 25"
f"Square root of 122: ${math.sqrt(122)}%1.4f" // "Square root of 122: 11.0454"
// Raw αλφαριθμητικά, που αγνοούν τους ειδικούς χαρακτήρες.
raw"New line feed: \n. Carriage return: \r." // => "New line feed: \n. Carriage return: \r."
// Μερικούς χαρακτήρες πρέπει να τους κάνουμε "escape",
// λ.χ ένα διπλό εισαγωγικό μέσα σε ένα αλφαριθμητικό :
"They stood outside the \"Rose and Crown\"" // => "They stood outside the "Rose and Crown""
/*
Τα τριπλά διπλά-εισαγωγικά επιτρέπουν στα αλφαριθμητικά να εκτείνονται σε
πολλαπλές γραμμές και να περιέχουν διπλά εισαγωγικά
*/
val html = """<form id="daform">
<p>Press belo', Joe</p>
<input type="submit">
</form>"""
/////////////////////////////////////////////////
// 2. Συναρτήσεις
/////////////////////////////////////////////////
// Οι συναρτήσεις ορίζονται ως εξής:
//
// def functionName(args...): ReturnType = { body... }
//
// Αν προέρχεστε απο πιο παραδοσιακές γλώσσες (C/C++ , Java) παρατηρήστε
// την παράλειψη του return. Στην Scala , η τελευταία έκφραση στο μπλόκ
// της συνάρτησης είναι η τιμή που επιστρέφει η συνάρτηση.
def sumOfSquares(x: Int, y: Int): Int = {
val x2 = x * x
val y2 = y * y
x2 + y2
}
// Τα { } μπορούν να παραλειφθούν αν η συνάρτηση αποτελείται απο μια απλή έκφραση:
def sumOfSquaresShort(x: Int, y: Int): Int = x * x + y * y
// Η σύνταξη για την κλήση συναρτήσεων είναι γνώριμη:
sumOfSquares(3, 4) // => 25
// Στις περισσότερες των περιπτώσεων (με τις αναδρομικές συναρτήσεις να αποτελούν
// την πιο αξιοπρόσεκτη εξαίρεση) , ο τύπος επιστροφής της συνάρτησης μπορεί να
// παραλειφθεί, και η ίδια συμπερασματολογία τύπων που είδαμε με τις μεταβλητές
// θα δουλεύει και με τους τύπους επιστροφής της συνάρτησης:
def sq(x: Int) = x * x // Ο μεταγλωττιστής μπορεί να μαντέψει ότι
// ο τύπος επιστροφής της συνάρτησης είναι Int
// Οι συναρτήσεις μπορούν να έχουν προκαθορισμένες τιμές:
def addWithDefault(x: Int, y: Int = 5) = x + y
addWithDefault(1, 2) // => 3
addWithDefault(1) // => 6
// Οι ανώνυμες συναρτήσεις είναι ως εξής:
(x:Int) => x * x
// Σε αντίθεση με τα defs , ακόμα και ο τύπος εισόδου απο τις ανώνυμες
// συναρτήσεις μπορεί να παραληφθεί αν τα συμφραζόμενα το κάνουν ξεκάθαρο.
// Προσέξτε τον τύπο "Int => Int" που σημαίνει ότι μια συνάρτηση παίρνει
// ένα Int και επιστρέφει ένα Int.
val sq: Int => Int = x => x * x
// Οι ανώνυμες συναρτήσεις μπορούν να κληθούν όπως συνήθως:
sq(10) // => 100
// Αν κάθε όρισμα στην ανώνυμη συνάρτηση χρησιμοποιείται μόνο μία φορά,
// η Scala επιτρέπει έναν ακόμα πιο σύντομο τρόπο να οριστεί. Αυτές
// οι ανώνυμες συναρτήσεις αποδεικνύεται ότι είναι πολύ κοινές ,
// όπως θα γίνει προφανές στο μέρος των δομών δεδομένων.
val addOne: Int => Int = _ + 1
val weirdSum: (Int, Int) => Int = (_ * 2 + _ * 3)
addOne(5) // => 6
weirdSum(2, 4) // => 16
// Η δεσμευμένη λέξη return υπάρχει στην Scala , αλλά επιστρέφει μόνο
// από το πιο εσωτερικό def που την περικλείει.
// ΠΡΟΣΟΧΗ: Η χρήση του return στην Scala είναι επιρρεπής σε λάθη
// και θα πρέπει να αποφεύγεται.
// Δεν έχει καμία επίδραση στις ανώνυμες συναρτήσεις. Για παράδειγμα:
def foo(x: Int): Int = {
val anonFunc: Int => Int = { z =>
if (z > 5)
return z // Αυτή η σειρά κάνει το z την τιμή που επιστρέφει η foo!
else
z + 2 // Αυτή η γραμμή είναι η τιμή που επιστρέφει η anonFunc
}
anonFunc(x) // Αυτή η γραμμή είναι η τιμή που επιστρέφει η foo
}
/////////////////////////////////////////////////
// 3. Έλεγχος ροής
/////////////////////////////////////////////////
1 to 5
val r = 1 to 5
r.foreach( println )
r foreach println
// ΠΡΟΣΟΧΗ: Η Scala είναι σχετικά επιεικής ως αναφορά τις τελείες και
// τις παρενθέσεις. Διαβάστε τους κανόνες ξεχωριστά.
// Αυτό βοηθάει στο να γράφεις DSLs και APIs που διαβάζονται σαν τα Αγγλικά.
(5 to 1 by -1) foreach ( println )
// Ένας βρόχος while :
var i = 0
while (i < 10) { println("i " + i); i+=1 }
while (i < 10) { println("i " + i); i+=1 } // Ναι ξανά! Τι συνέβη; Γιατί;
i // Εμφάνισε την τιμή του i. Σημειώστε ότι ένας βρόχος while είναι βρόχος
// με την κλασική έννοια - εκτελείται σειριακά καθώς αλλάζει η μεταβλητή
// του βρόχου. Το while είναι πολύ γρήγορο , γρηγορότερο απο τους βρόχους
// της Java , αλλά η χρήση combinators και comprehensions όπως πιο πάνω ,
// είναι πιο εύκολη στην κατανόηση και στην παραλληλοποίηση.
// Ένας βρόχος do while :
do {
println("x is still less than 10");
x += 1
} while (x < 10)
// Η αναδρομή ουράς είναι ένας ιδιωματικός τρόπος να κάνεις επαναλαμβανόμενα
// πράγματα στην Scala. Οι αναδρομικές συναρτήσεις απαιτούν να γραφτεί
// ρητά ο τύπος που θα επιστρέψουν, αλλιώς ο μεταγλωττιστής δεν μπορεί
// αλλιώς να τον συνάγει. Παρακάτω είναι μια συνάρτηση που επιστρέφει Unit.
def showNumbersInRange(a:Int, b:Int):Unit = {
print(a)
if (a < b)
showNumbersInRange(a + 1, b)
}
showNumbersInRange(1,14)
// Έλεγχος Ροής
val x = 10
if (x == 1) println("yeah")
if (x == 10) println("yeah")
if (x == 11) println("yeah")
if (x == 11) println ("yeah") else println("nay")
println(if (x == 10) "yeah" else "nope")
val text = if (x == 10) "yeah" else "nope"
/////////////////////////////////////////////////
// 4. Δομές Δεδομένων
/////////////////////////////////////////////////
val a = Array(1, 2, 3, 5, 8, 13)
a(0)
a(3)
a(21) // "Πετάει" exception
val m = Map("fork" -> "tenedor", "spoon" -> "cuchara", "knife" -> "cuchillo")
m("fork")
m("spoon")
m("bottle") // "Πετάει" exception
val safeM = m.withDefaultValue("no lo se")
safeM("bottle")
val s = Set(1, 3, 7)
s(0)
s(1)
/* Δείτε το documentation του map εδώ -
* http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map
*/
// Πλειάδες
(1, 2)
(4, 3, 2)
(1, 2, "three")
(a, 2, "three")
// Γιατί να το έχουμε αυτό;
val divideInts = (x:Int, y:Int) => (x / y, x % y)
divideInts(10,3) // Η συνάρτηση divideInts επιστρέφει το αποτέλεσμα
// της ακέραιας διαίρεσης και το υπόλοιπο.
// Για να έχουμε πρόσβαση στα στοιχεία μιας πλειάδας, χρησιμοποιούμε το _._n
// όπου το n είναι ο δείκτης με βάση το 1 του στοιχείου.
val d = divideInts(10,3)
d._1
d._2
/////////////////////////////////////////////////
// 5. Αντικειμενοστραφής Προγραμματισμός
/////////////////////////////////////////////////
/*
Ότι έχουμε κάνει ως τώρα σε αυτό το tutorial ήταν απλές εκφράσεις
(τιμές, συναρτήσεις, κτλ.). Αυτές οι εκφράσεις βολεύουν όταν τις
γράφουμε στο REPL για γρήγορες δοκιμές, αλλά δεν μπορούν να υπάρχουν
από μόνες τους σε ένα αρχείο Scala. Για παράδειγμα , δεν μπορούμε να
έχουμε μόνο ένα "val x = 5" στο αρχείο Scala. Αντί αυτού , τα μόνα
στοιχεία του πάνω επιπέδου που επιτρέπονται στην Scala είναι:
- αντικείμενα (objects)
- κλάσεις (classes)
- κλάσεις περίπτωσης (case classes στην Scala)
- Χαρακτηριστικά (traits , όπως ονομάζονται στην Scala)
Και τώρα θα εξηγήσουμε τι είναι αυτά.
*/
// Οι κλάσεις είναι παρόμοιες με τις κλάσεις σε άλλες γλώσσες. Τα ορίσματα του
// "κατασκευαστή" (constructor) δηλώνονται μετά από το όνομα της κλάσης ,
// και η αρχικοποιήση γίνεται μέσα στο σώμα της κλάσης.
class Dog(br: String) {
// Κώδικας για τον "κατασκευαστή"
var breed: String = br
// Ορίζεται μια μέθοδος bark , που επιστρέφει ένα αλφαριθμητικό
def bark = "Woof, woof!"
// Οι τιμές και οι μέθοδοι είναι public εκτός αν χρησιμοποιήσουμε κάποια
// απο τις λέξεις κλειδιά "protected" και "private" .
private def sleep(hours: Int) =
println(s"I'm sleeping for $hours hours")
// Οι abstract μέθοδοι είναι απλά μέθοδοι χωρίς σώμα. Αν βγάζαμε
// το σχόλιο απο την επόμενη γραμμή η κλάση Dog θα έπρεπε να
// δηλωθεί ως abstract class Dog(...) { ... } :
// def chaseAfter(what: String): String
}
val mydog = new Dog("greyhound")
println(mydog.breed) // => "greyhound"
println(mydog.bark) // => "Woof, woof!"
// Η λέξη "object" δημιουργεί ένα type ΚΑΙ ένα singleton instance αυτού.
// Είναι κοινό για τις κλάσεις στην Scala να έχουν ένα "συντροφικό object",
// όπου η συμπεριφορά για κάθε instance αιχμαλωτίζεται μέσα στις κλάσεις
// αυτές καθ' αυτές, αλλά η συμπρεριφορά που σχετίζεται με όλα τα instances
// της κλάσης πάνε μέσα στο object. Η διαφορά είναι παρόμοια με τις
// μεθόδους κλάσεων σε σχέση με στατικές μεθόδους σε άλλες γλώσσες.
// Προσέξτε ότι τα objects και οι κλάσεις μπορούν να έχουν το ίδιο όνομα.
object Dog {
def allKnownBreeds = List("pitbull", "shepherd", "retriever")
def createDog(breed: String) = new Dog(breed)
}
// Οι κλάσεις περίπτωσης (case classes) είναι που έχουν την επιπλέον
// λειτουργικότητα ενσωματωμένη. Μιά συνήθης ερώτηση για αρχάριους στην
// Scala είναι πότε να χρησιμοποιούνται κλάσεις και πότε case κλάσεις.
// Γενικά οι κλάσεις τείνουν να εστιάζουν στην ενθυλάκωση, τον
// πολυμορφισμό και τη συμπεριφορά. Οι τιμές μέσα σε αυτές τις κλάσεις
// τείνουν να είναι private , και μόνο οι μέθοδοι είναι εκτεθειμένες.
// Ο κύριος σκοπός των case classes είναι να κρατούν δεδομένα που είναι
// σταθερές(immutable). Συνήθως έχουν λίγες μεθόδους και οι μέθοδοι σπάνια
// έχουν παρενέργειες.
case class Person(name: String, phoneNumber: String)
// Δημιουργία ενός instance. Παρατηρήστε ότι τα case classes
// δεν χρειάζονται την λέξη "new" .
val george = Person("George", "1234")
val kate = Person("Kate", "4567")
// Με τα case classes, παίρνεις μερικά προνόμια δωρεάν , όπως:
george.phoneNumber // => "1234"
// Ελέγχεται η ισότητα για κάθε πεδίο (δεν χρειάζεται να
// κάνουμε override στο .equals)
Person("George", "1234") == Person("Kate", "1236") // => false
// Έυκολος τρόπος να κάνουμε αντιγραφή. Δημιουργούμε έναν νέο geroge:
// otherGeorge == Person("george", "9876")
val otherGeorge = george.copy(phoneNumber = "9876")
// Και πολλά άλλα. Τα case classes έχουν και αντιστοίχιση προτύπων
// (pattern matching) δωρεάν, δείτε παρακάτω.
// Τα χαρακτηριστικά (traits) έρχονται σε λίγο καιρό !
/////////////////////////////////////////////////
// 6. Αντιστοίχιση Προτύπων
/////////////////////////////////////////////////
// Η αντιστοίχιση προτύπων (pattern matching) είναι ένα πολύ δυνατό και
// ευρέως χρησιμοποιούμενο χαρακτηριστικό στην Scala. Παρακάτω βλέπουμε
// πως γίνεται το pattern matching σε ένα case class. Σημείωση: Σε
// αντίθεση με άλλες γλώσσες η Scala δεν χρειάζεται breaks, γιατί γίνεται
// αυτόματα όταν γίνει κάποιο match.
def matchPerson(person: Person): String = person match {
// Μετά προσδιορίζουμε το πρότυπο (pattern):
case Person("George", number) => "We found George! His number is " + number
case Person("Kate", number) => "We found Kate! Her number is " + number
case Person(name, number) => "We matched someone : " + name + ", phone : " + number
}
val email = "(.*)@(.*)".r // Ορίζουμε ένα regex για το επόμενο παράδειγμα.
// (regex <- REGular EXpression)
// Το pattern matching μπορεί να μοιάζει γνώριμο απο τα switch statements σε
// γλώσσες που ανήκουν στην οικογένεια της C αλλά είναι πολύ πιο ισχυρό.
// Στην Scala , μπορούμε να κάνουμε match πολύ περισσότερα:
def matchEverything(obj: Any): String = obj match {
// Μπορούμε να ταιριάξουμε τιμές:
case "Hello world" => "Got the string Hello world"
// Μπορούμε να ταιριάξουμε τύπους:
case x: Double => "Got a Double: " + x
// Μπορούμε να βάλουμε συνθήκες:
case x: Int if x > 10000 => "Got a pretty big number!"
// Μπορούμε να ταιριάξουμε case classes όπως πρίν:
case Person(name, number) => s"Got contact info for $name!"
// Μπορούμε να ταιριάξουμε regex:
case email(name, domain) => s"Got email address $name@$domain"
// Μπορούμε να ταιριάξουμε πλειάδες:
case (a: Int, b: Double, c: String) => s"Got a tuple: $a, $b, $c"
// Μπορούμε να ταιριάξουμε δομές δεδομένων:
case List(1, b, c) => s"Got a list with three elements and starts with 1: 1, $b, $c"
// Μπορούμε να ταιριάξουμε πρότυπα που το ένα είναι μέσα στο άλλο:
case List(List((1, 2,"YAY"))) => "Got a list of list of tuple"
}
// Στην πραγματικότητα , μπορούμε να κάνουμε pattern matching σε όποιο αντικείμενο
// έχει την μέθοδο "unapply". Αυτό το χαρακτηριστικό είναι τόσο ισχυρό ώστε
// η Scala επιτρέπει να ορίστούν ολόκληρες συναρτήσεις σαν patterns.
val patternFunc: Person => String = {
case Person("George", number) => s"George's number: $number"
case Person(name, number) => s"Random person's number: $number"
}
/////////////////////////////////////////////////
// 7. Συναρτησιακός Προγραμματισμός
/////////////////////////////////////////////////
// Η Scala επιτρέπει στις μεθόδους και τις συναρτήσεις να επιστρέφουν ή να
// δέχονται ως παραμέτρους άλλες μεθόδους ή συναρτήσεις.
val add10: Int => Int = _ + 10 // Μια συνάρτηση που δέχεται Int και επιστρέφει Int
List(1, 2, 3) map add10 // List(11, 12, 13) - το add10 εφαρμόζεται σε κάθε στοιχείο
// μέσω του map
// Οι ανώνυμες συναρτήσεις μπορούν να χρησιμοποιηθούν αντί
// ονοματισμένων (όπως απο πάνω) :
List(1, 2, 3) map (x => x + 10)
// Και το σύμβολο της κάτω παύλας , μπορεί να χρησιμοποιηθεί αν υπάρχει μόνο
// ένα όρισμα στην ανώνυμη συνάρτηση. Έτσι δεσμεύεται ως η μεταβλητή.
List(1, 2, 3) map (_ + 10)
// Αν το μπλοκ της ανώνυμης συνάρτησης ΚΑΙ η συνάρτηση που εφαρμόζεται
// (στην περίπτωσή μας το foreach και το println) παίρνουν ένα όρισμα
// μπορείτε να παραλείψετε την κάτω παύλα.
List("Dom", "Bob", "Natalia") foreach println
// Συνδυαστές
s.map(sq)
val sSquared = s. map(sq)
sSquared.filter(_ < 10)
sSquared.reduce (_+_)
// Η συνάρτηση filter παίρνει ένα κατηγορούμενο (predicate)
// που είναι μια συνάρτηση απο το A -> Boolean και διαλέγει
// όλα τα στοιχεία που ικανοποιούν αυτό το κατηγορούμενο.
List(1, 2, 3) filter (_ > 2) // List(3)
case class Person(name:String, age:Int)
List(
Person(name = "Dom", age = 23),
Person(name = "Bob", age = 30)
).filter(_.age > 25) // List(Person("Bob", 30))
// Το foreach είναι μια μέθοδος της Scala , που ορίζεται για ορισμένες
// συλλογές (collections). Παίρνει έναν τύπο και επιστρέφει Unit
// (μια μέθοδο void)
val aListOfNumbers = List(1, 2, 3, 4, 10, 20, 100)
aListOfNumbers foreach (x => println(x))
aListOfNumbers foreach println
// For comprehensions
for { n <- s } yield sq(n)
val nSquared2 = for { n <- s } yield sq(n)
for { n <- nSquared2 if n < 10 } yield n
for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared
/*
Προσοχή : Αυτά δεν ήταν βρόχοι for. Η σημασιολογία ενός βρόχου for είναι
η επανάληψη, ενώ ένα for-comprehension ορίζει μια σχέση μεταξύ δύο
συνόλων δεδομένων.
*/
/////////////////////////////////////////////////
// 8. Implicits
/////////////////////////////////////////////////
/*
ΠΡΟΣΟΧΗ! Τα implicits είναι ένα σύνολο απο ισχυρά χαρακτηριστικά της Scala
και επομένως είναι εύκολο να γίνει κατάχρηση. Οι αρχάριοι στην Scala θα
πρέπει να αντισταθούν στον πειρασμό να τα χρησιμοποιήσουν έως ότου, όχι
μόνο καταλάβουν πως λειτουργούν, αλλά ακόμα εξασκηθούν πάνω τους.
Ο μόνος λόγος που συμπεριλάβαμε αυτό το κομμάτι στο tutorial είναι
γιατί είναι τόσο κοινό στις βιβλιοθήκες της Scala , που αδύνατο να κάνεις
οτιδήποτε σημαντικό χωρίς να χρησιμοποιήσεις μια που να έχει implicits.
*/
// Κάθε τιμή (vals , συναρτήσεις , αντικείμενα , κτλ) μπορεί να δηλωθεί ως
// implicit χρησιμοποιώντας , ναι το μαντέψατε , την λέξη "implicit".
// Σημειώστε ότι χρησιμοποιούμε την κλάση Dog που δημιουργήσαμε στο
// 5ο μέρος των παραδειγμάτων.
implicit val myImplicitInt = 100
implicit def myImplicitFunction(breed: String) = new Dog("Golden " + breed)
// Απο μόνη της, η λέξη implicit, δεν αλλάζει την συμπεριφορά μιάς τιμής
// οπότε οι παραπάνω μπορούν να χρησιμοποιοηθούν όπως συνήθως.
myImplicitInt + 2 // => 102
myImplicitFunction("Pitbull").breed // => "Golden Pitbull"
// Η διαφορά είναι ότι τώρα αυτές οι τιμές έχουν την δυνατότητα να
// χρησιμοποιηθούν όταν ένα άλλο κομμάτι κώδικα "χρειάζεται" μια
// implicit τιμή. Μια τέτοια περίπτωση είναι τα ορίσματα μιας implicit
// συνάρτησης:
def sendGreetings(toWhom: String)(implicit howMany: Int) =
s"Hello $toWhom, $howMany blessings to you and yours!"
// Άν τροφοδοτήσουμε μια τιμή για το "homMany", η συνάρτηση συμπεριφέρεται
// ως συνήθως
sendGreetings("John")(1000) // => "Hello John, 1000 blessings to you and yours!"
// Αλλά αν παραλείψουμε την παράμετρο implicit , μια implicit τιμή του ιδίου τύπου
// χρησιμοποιείται, στην περίπτωσή μας, το "myImplicitInt"
sendGreetings("Jane") // => "Hello Jane, 100 blessings to you and yours!"
// Οι παράμετροι implicit συναρτήσεων μας επιτρέπουν να προσομοιάζουμε
// κλάσεις τύπων (type classes) σε άλλες συναρτησιακές γλώσσες.
// Χρησιμοποιείται τόσο συχνά που έχει την δικιά του συντομογραφία.
// Οι επόμενες δύο γραμμές κώδικα σημαίνουν το ίδιο πράγμα.
def foo[T](implicit c: C[T]) = ...
def foo[T : C] = ...
// Μια άλλη περίπτωση στην οποία ο μεταγλωττιστής αναζητά μια implicit τιμή
// είναι αν έχετε obj.method (...)
// αλλά το "obj" δεν έχει την "method" ως μέθοδο. Σε αυτή την περίπτωση,
// αν υπάρχει μια implicit μετατροπή του τύπου Α => Β, όπου Α είναι ο τύπος
// του obj, ενώ το Β έχει μία μέθοδο που ονομάζεται «method», εφαρμόζεται η
// εν λόγω μετατροπή. Έτσι, έχοντας την MyImplicitFunction μέσα στο πεδίο
// εφαρμογής(scope), μπορούμε να πούμε:
"Retriever".breed // => "Golden Retriever"
"Sheperd".bark // => "Woof, woof!"
// Εδώ το String αρχικά μετατρέπεται σε Dog χρησιμοποιώντας την συνάρτησή μας
// παραπάνω, και μετά καλείται η κατάλληλη μέθοδος. Αυτό είναι ένα εξερετικά
// ισχυρό χαρακτηριστικό, αλλά δεν πρέπει να χρησιμοποιείται με ελαφριά την
// καρδιά. Μάλιστα, όταν ορίσατε την συνάρτηση implicit παραπάνω, ο μεταγλωττιστής
// θα πρέπει να σας έδωσε μια προειδοποιήση, ότι δεν πρέπει να το κάνετε αυτό
// εκτός αν πραγματικά γνωρίζετε τι κάνετε.
/////////////////////////////////////////////////
// 9. Διάφορα
/////////////////////////////////////////////////
// Εισαγωγή βιβλιοθηκών κτλ
import scala.collection.immutable.List
// Εισαγωγή των πάντων απο το scala.collection.immutable
import scala.collection.immutable._
// Εισαγωγή πολλών κλάσεων σε μία έκφραση
import scala.collection.immutable.{List, Map}
// Δώστε ένα νέο όνομα στην εισαγωγή σας χρησιμοποιώντας το '=>'
import scala.collection.immutable.{ List => ImmutableList }
// Εισαγωγή όλων των κλάσεων εκτός απο μερικές.
// Το επόμενο δεν εισάγει το Map και το Set:
import scala.collection.immutable.{Map => _, Set => _, _}
// Το σημείο εισαγωγής του προγράμματος σας ορίζεται σε ένα αρχείο scala ,
// χρησιμοποιώντας ένα αντικείμενο (object), με μία μέθοδο , την main.
object Application {
def main(args: Array[String]): Unit = {
// Εδω γράφουμε ...
}
}
// Files can contain multiple classes and objects. Compile with scalac
// Τα files μπορούν να περιέχουν περισσότερες απο μία κλάσεις και
// αντικείμενα. Το compile γίνεται με την εντολή scalac
// Εισαγωγή και εξαγωγή.
// Για να διβάσετε ένα αρχείο γραμμή προς γραμμή
import scala.io.Source
for(line <- Source.fromFile("myfile.txt").getLines())
println(line)
// Για να γράψετε σε ένα αρχείο
val writer = new PrintWriter("myfile.txt")
writer.write("Writing line for line" + util.Properties.lineSeparator)
writer.write("Another line here" + util.Properties.lineSeparator)
writer.close()
```
## Further resources
[Scala for the impatient](http://horstmann.com/scala/)
[Twitter Scala school](http://twitter.github.io/scala_school/)
[The scala documentation](http://docs.scala-lang.org/)
[Try Scala in your browser](http://scalatutorials.com/tour/)
Join the [Scala user group](https://groups.google.com/forum/#!forum/scala-user)
|