1
0
mirror of https://github.com/adambard/learnxinyminutes-docs.git synced 2025-07-31 12:00:34 +02:00

added the translations

This commit is contained in:
billpcs
2015-08-05 16:40:37 +03:00
parent fded8fbd6c
commit f27ed65af6
2 changed files with 1436 additions and 0 deletions

View File

@@ -0,0 +1,746 @@
---σ
language: racket
filename: learnracket.rkt
contributors:
- ["th3rac25", "https://github.com/voila"]
- ["Eli Barzilay", "https://github.com/elibarzilay"]
- ["Gustavo Schmidt", "https://github.com/gustavoschmidt"]
- ["Duong H. Nguyen", "https://github.com/cmpitg"]
- ["Keyan Zhang", "https://github.com/keyanzhang"]
translators:
- ["Vasilis Panagiotopoulos" , "https://github.com/billpcs/"]
---
Racket is a general purpose, multi-paradigm programming language in the Lisp/Scheme family.
Feedback is appreciated! You can reach me at [@th3rac25](http://twitter.com/th3rac25) or th3rac25 [at] [google's email service]
```racket
#lang racket ; ορίζει την γλώσσα που χρησιμοποιόυμε
;;; Σχόλια
;; Τα σχόλια μιας γραμμής ξεκινούν με ερωτηματικό
#| Τα σχόλια ολόκληρου μπλόκ
μπορούν να εκτείνονται σε πολλές γραμμές και...
#|
μπορούν να είναι εμφωλευμένα!
|#
|#
;; Τα σχόλια S-expression (εκφράσεις S) comments απορρίπτουν την
;; έκφραση που ακολουθεί, δυνατότητα που είναι χρήσιμη για να
;; κάνουμε σχόλια κάποιες εκφράσεις κατα τη διάρκεια του debugging
#; (αυτή η έκφραση δεν θα εκτελεστεί)
;; (Αν δεν καταλαβαίνεται τι είναι οι εκφράσεις , περιμένετε... Θα το μάθουμε
;; πολύ συντομα!)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 1. Πρωτογενείς τύποι μεταβλητών και τελεστές
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Αριθμοί
9999999999999999999999 ; ακέραιοι
#b111 ; δυαδικοί => 7
#o111 ; οκταδικοί => 73
#x111 ; δεκαεξαδικοί => 273
3.14 ; πραγματικοί
6.02e+23
1/2 ; ρητοί
1+2i ; μιγαδικοί
;; Οι μορφή των συναρτήσεων είναι (f x y z)
;; όπου το f είναι η συνάρτηση και τα x y z
;; είναι οι όροι που η συνάρτηση δέχεται
;; ως ορίσματα. Αν θέλουμε να δημιουργήσουμε
;; μια λίστα στην κυριολεξία απο δίαφορα δεδομένα,
;; χρησιμοποιούμε το ' για να το εμποδίσουμε απο το να
;; αξιολογηθεί σαν έκφραση. Για παράδειγμα:
'(+ 1 2) ; => Παραμένει (+ 1 2) και δεν γίνεται η πράξη
;; Τώρα , ας κάνουμε μερικές πράξεις
(+ 1 1) ; => 2
(- 8 1) ; => 7
(* 10 2) ; => 20
(expt 2 3) ; => 8
(quotient 5 2) ; => 2
(remainder 5 2) ; => 1
(/ 35 5) ; => 7
(/ 1 3) ; => 1/3
(exact->inexact 1/3) ; => 0.3333333333333333
(+ 1+2i 2-3i) ; => 3-1i
;;; Λογικές μεταβλητές
#t ; για το true
#f ; για το false
(not #t) ; => #f
(and 0 #f (error "doesn't get here")) ; => #f
(or #f 0 (error "doesn't get here")) ; => 0
;;; Χαρακτήρες
#\A ; => #\A
#\λ ; => #\λ
#\u03BB ; => #\λ
;;; Τα αλφαριθμητικά είναι πίνακες χαρακτήρων συγκεκριμένου μήκους
"Hello, world!"
"Benjamin \"Bugsy\" Siegel" ; Το backslash είναι χαρακτήρας διαφυγής
"Foo\tbar\41\x21\u0021\a\r\n" ; συμπεριλαμβάνονται οι χαρακτήες διαφυγής της C,
; σε Unicode
"λx:(μα.α→α).xx" ; μπορούν να υπάρχουν και Unicode χαρακτήρες
;; Μπορούμε να εννώσουμε αλφαριθμητικά!
(string-append "Hello " "world!") ; => "Hello world!"
;; Ένα αλφαριθμητικό μπορούμε να το χρησιμοπιησουμε
;; όπως και μια λίστα απο χαρακτήρες
(string-ref "Apple" 0) ; => #\A ;; Παίρνουμε το πρώτο στοιχείο
;; Η συνάρτηση format μπορεί να χρησιμοποιηθεί για
;; να μορφοποιήσουμε αλφαριθμητικά
(format "~a can be ~a" "strings" "formatted") ;; => "strings can be formatted"
;; Η εκτύπωση είναι εύκολη.
(printf "I'm Racket. Nice to meet you!\n")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 2. Μεταβλητές
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; You can create a variable using define
;; a variable name can use any character except: ()[]{}",'`;#|\
(define some-var 5)
some-var ; => 5
;; You can also use unicode characters
(define subset?) ;; Εδώ ουστιαστικά δίνουμε στη ήδη ύπαρχουσα συνάρτηση subset?
;; ένα νέο όνομα ⊆ , και παρακάτω την καλούμε με το νέο της όνομα.
( (set 3 2) (set 1 2 3)) ; => #t
;; Αν ζητήσουμε μια μεταβλητή που δεν έχει οριστεί πρίν π.χ
(printf name)
;; θα πάρουμε το παρακάτω μήνυμα
;name: undefined;
; cannot reference undefined identifier
; context...:
;; Η τοπική δέσμευση : `me' δευσμεύεται με το "Bob" μόνο μέσα στο (let ...)
(let ([me "Bob"])
"Alice"
me) ; => "Bob"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. Δομές και συλλογές
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Δομές
(struct dog (name breed age))
(define my-pet
(dog "lassie" "collie" 5))
my-pet ; => #<dog>
(dog? my-pet) ; => #t
(dog-name my-pet) ; => "lassie"
;;; Ζεύγη (αμετάβλητα)
;; Η δεσμευμένη λέξη `cons' δημιουργεί ζεύγη,
;; και το `car' και το `cdr' εξάγουν το πρώτο και
;; το δεύτερο στοιχείο αντίστοιχα.
(cons 1 2) ; => '(1 . 2)
(car (cons 1 2)) ; => 1
(cdr (cons 1 2)) ; => 2
;;; Λίστες
;; Οι λίστες είναι linked-list δομές δεδομένων,
;; που έχουν δημιουργηθεί απο ζευγάρια 'cons'
;; και τελειώνουν με 'null' (ή αλλιώς '()) για να
;; δηλώσουν ότι αυτό είναι το τέλος της λίστας
(cons 1 (cons 2 (cons 3 null))) ; => '(1 2 3)
;; Η δεσμευμένη λέξη 'list' είναι ένας εναλλακτικός
;; (και σαφώς πιο βολικός) τρόπος για να δημιουργούμε
;; λίστες
(list 1 2 3) ; => '(1 2 3)
;; αλλά και χρησιμοποιώντας ένα μονό εισαγωγικό το
;; το αποτέλεσμα είναι και πάλι το ίδιο
'(1 2 3) ; => '(1 2 3)
;; Μπορούμε και πάλι όμως να χρησιμοποιούμε το 'cons' για να
;; προσθέσουμε ένα στοιχείο στην αρχή της λίστας
(cons 4 '(1 2 3)) ; => '(4 1 2 3)
;; Μπορούμε να χρησιμοποιούμε το 'append' για να προσθέτουμε
;; στοιχεία στο τέλος μιας λίστας. Το στοιχείο αυτό μπορεί
;; και να είναι ολόκληρη λίστα!
(append '(1 2) '(3 4)) ; => '(1 2 3 4)
;; Οι λίστες στην Racket είναι πολύ βασικές , οπότε υπάρχουν πολλές
;; δυνατές λειτουργίες για αυτές. Παρακάτω είναι μερικά παραδείγματα:
(map add1 '(1 2 3)) ; => '(2 3 4)
(map + '(1 2 3) '(10 20 30)) ; => '(11 22 33)
(filter even? '(1 2 3 4)) ; => '(2 4)
(count even? '(1 2 3 4)) ; => 2
(take '(1 2 3 4) 2) ; => '(1 2)
(drop '(1 2 3 4) 2) ; => '(3 4)
;;; Διανύσματα
;; Τα διανύσματα είναι πίνακες σταθερού μήκους
#(1 2 3) ; => '#(1 2 3)
;; Χρησιμοποιύμε το `vector-append' για να προσθέσουμε διανύσματα
(vector-append #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)
;;; Σύνολα
;; Δημιουργούμε ένα σύνολο απο μία λίστα
(list->set '(1 2 3 1 2 3 3 2 1 3 2 1)) ; => (set 1 2 3)
;; Προσθέτουμε έναν αριθμό στο σύνολο χρησιμοποιώντας το `set-add'
(set-add (set 1 2 3) 4) ; => (set 1 2 3 4)
;; Αφαιρούμε με το `set-remove'
(set-remove (set 1 2 3) 1) ; => (set 2 3)
;; Βλέπουμε αν υπάρχει ένας αριθμός στο σύνολο με το `set-member?'
(set-member? (set 1 2 3) 1) ; => #t
(set-member? (set 1 2 3) 4) ; => #f
;;; Πίνακες κατακερματισμού
;; Δημιουργήστε ένα αμετάβλητο πίνακα κατακερματισμού
(define m (hash 'a 1 'b 2 'c 3))
;; Παίρνουμε μια τιμή απο τον πίνακα
(hash-ref m 'a) ; => 1
;; Άν ζητήσουμε μια τιμή που δέν υπάρχει παίρνουμε μία εξαίρεση
; (hash-ref m 'd) => no value found for key
;; Μπορούμε να δώσουμε μια default τιμή για τα κλειδιά που λείπουν
(hash-ref m 'd 0) ; => 0
;; Χρησιμοποιούμε το 'hash-set' για να επεκτείνουμε
;; ένα πίνακα κατακερματισμού
(define m2 (hash-set m 'd 4))
m2 ; => '#hash((b . 2) (a . 1) (d . 4) (c . 3))
;; Θυμηθείτε ! Αυτοί οι πίνακες κατακερματισμού
;; είναι αμετάβλητοι!
m ; => '#hash((b . 2) (a . 1) (c . 3)) <-- δεν υπάρχει `d'
;; Χρησιμοποιούμε το `hash-remove' για να αφαιρέσουμε
;; κλειδία
(hash-remove m 'a) ; => '#hash((b . 2) (c . 3))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. Συναρτήσεις
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Χρησιμοποιούμε το `lambda' για να δημιουργήσουμε συναρτήσεις.
;; Μια συνάρτηση πάντα επιστρέφει την τιμή της τελευταίας της έκφρασης
(lambda () "Hello World") ; => #<procedure>
;; Μπορούμε επίσης να χρησιμοποιήσουμε το `λ'
(λ () "Hello World") ; => Ίδια συνάρτηση
;; Χρησιμοποιύμε τις παρενθέσεις για να καλέσουμε όλες τις συναρτήσεις
;; συμπεριλαμβανομένων και των εκφράσεων 'λάμδα'
((lambda () "Hello World")) ; => "Hello World"
((λ () "Hello World")) ; => "Hello World"
;; Εκχωρούμε σε μια μετάβλητη την συνάρτηση
(define hello-world (lambda () "Hello World"))
(hello-world) ; => "Hello World"
;; Μπορούμε αυτό να το κάνουμε συντομότερο χρησιμοποιώντας
;; το λεγόμενο syntactic sugar :
(define (hello-world2) "Hello World")
;; Το () στο παραπάνω είναι η λίστα από τα ορίσματα για την συνάρτηση
(define hello
(lambda (name)
(string-append "Hello " name)))
(hello "Steve") ; => "Hello Steve"
;; ... ή ισοδύναμα, χρησιμοποιώντας sugared ορισμό:
(define (hello2 name)
(string-append "Hello " name))
;; Μπορούμε να έχουμε συναρτήσεις με πολλές μεταβλητές χρησιμοποιώντας
;; το `case-lambda'
(define hello3
(case-lambda
[() "Hello World"]
[(name) (string-append "Hello " name)]))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"
;; ... ή να ορίσουμε προαιρετικά ορίσματα με μια έκφραση προκαθορισμένης τιμής
(define (hello4 [name "World"])
(string-append "Hello " name))
;; Οι συναρτήσεις μπορούν να πακετάρουν επιπλέον
;; ορίσματα μέσα σε μια λίστα
(define (count-args . args)
(format "You passed ~a args: ~a" (length args) args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
;; ... ή με unsugared μορφή `lambda':
(define count-args2
(lambda args
(format "You passed ~a args: ~a" (length args) args)))
;; Μπορούμε να εμπλέξουμε κανονικά και πακεταρισμένα ορίσματα
(define (hello-count name . args)
(format "Hello ~a, you passed ~a extra args" name (length args)))
(hello-count "Finn" 1 2 3)
; => "Hello Finn, you passed 3 extra args"
;; ... και unsugared:
(define hello-count2
(lambda (name . args)
(format "Hello ~a, you passed ~a extra args" name (length args))))
;; Και με λέξεις κλειδία
(define (hello-k #:name [name "World"] #:greeting [g "Hello"] . args)
(format "~a ~a, ~a extra args" g name (length args)))
(hello-k) ; => "Hello World, 0 extra args"
(hello-k 1 2 3) ; => "Hello World, 3 extra args"
(hello-k #:greeting "Hi") ; => "Hi World, 0 extra args"
(hello-k #:name "Finn" #:greeting "Hey") ; => "Hey Finn, 0 extra args"
(hello-k 1 2 3 #:greeting "Hi" #:name "Finn" 4 5 6)
; => "Hi Finn, 6 extra args"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 4. Ισότητα
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; για αριθμούς χρησιμοποιούμε το `='
(= 3 3.0) ; => #t
(= 2 1) ; => #f
;; Το `eq?' επιστρέφει #t αν δύο 2 ορίσματα αναφέρονται στο
;; ίδιο αντικείμενο (στη μνήμη),αλλιώς επιστρέφει #f.
;; Με άλλα λόγια, είναι απλή σύγκριση δεικτών.
(eq? '() '()) ; => #t, αφού υπάρχει μόνο μια άδεια λίστα στη μνήμη
(let ([x '()] [y '()])
(eq? x y)) ; => #t, το ίδιο με πάνω
(eq? (list 3) (list 3)) ; => #f
(let ([x (list 3)] [y (list 3)])
(eq? x y)) ; => #f — δεν είναι η ίδια λίστα στην μνήμη!
(let* ([x (list 3)] [y x])
(eq? x y)) ; => #t, Αφού το x και το y τώρα δείχνουν στην ίδια θέση
(eq? 'yes 'yes) ; => #t
(eq? 'yes 'no) ; => #f
(eq? 3 3) ; => #t — να είστε προσεκτικοί εδώ
; Είναι προτιμότερο να χρησιμοποιείτε `=' για την
; σύγκριση αριθμών.
(eq? 3 3.0) ; => #f
(eq? (expt 2 100) (expt 2 100)) ; => #f
(eq? (integer->char 955) (integer->char 955)) ; => #f
(eq? (string-append "foo" "bar") (string-append "foo" "bar")) ; => #f
;; Το `eqv?' υποστηρίζει την σύκριση αριθμών αλλα και χαρακτήρων
;; Για άλλα ήδη μεταβλητών το `eqv?' και το `eq?' επιστρέφουν το ίδιο.
(eqv? 3 3.0) ; => #f
(eqv? (expt 2 100) (expt 2 100)) ; => #t
(eqv? (integer->char 955) (integer->char 955)) ; => #t
(eqv? (string-append "foo" "bar") (string-append "foo" "bar")) ; => #f
;; Το `equal?' υποστηρίζει την σύγκριση των παρακάτω τύπων μεταβλητών:
;; `equal?' supports the comparison of the following datatypes:
;; αλφαριθμητικά, αλφαριθμητικά από bytes, μεταβλητά ζεύγη , διανύσματα,
;; πίνακες κατακερματισμού και δομές.
;; Για άλλα ήδη τύπων μεταβλητών το `equal?' και το `eqv?' επιστρέφουν το
;; ίδιο αποτέλεσμα.
(equal? 3 3.0) ; => #f
(equal? (string-append "foo" "bar") (string-append "foo" "bar")) ; => #t
(equal? (list 3) (list 3)) ; => #t
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 5. Έλεχγος Ροής
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Συνθήκες (conditionals)
(if #t ; έκφραση ελέχγου
"this is true" ; έκφραση then
"this is false") ; έκφραση else
; => "this is true"
;; Στα conditionals, όλες οι μη #f τιμές θεωρούνται ως #t
(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(Groucho Zeppo)
(if (member 'Groucho '(Harpo Groucho Zeppo))
'yep
'nope)
; => 'yep
;; Οι αλυσίδες `cond' είναι σειρές από ελέγχους για να
;; επιλεγεί ένα αποτέλεσμα
(cond [(> 2 2) (error "wrong!")]
[(< 2 2) (error "wrong again!")]
[else 'ok]) ; => 'ok
;;; Αντιστοίχιση μοτίβων
(define (fizzbuzz? n)
(match (list (remainder n 3) (remainder n 5))
[(list 0 0) 'fizzbuzz]
[(list 0 _) 'fizz]
[(list _ 0) 'buzz]
[_ #f]))
(fizzbuzz? 15) ; => 'fizzbuzz
(fizzbuzz? 37) ; => #f
;;; Βρόχοι
;; Οι επαναλήψεις μπορούν να γίνουν μέσω αναδρομής
(define (loop i)
(when (< i 10)
(printf "i=~a\n" i)
(loop (add1 i))))
(loop 5) ; => i=5, i=6, ...
;; Παρομοίως με τη χρήση 'let'
(let loop ((i 0))
(when (< i 10)
(printf "i=~a\n" i)
(loop (add1 i)))) ; => i=0, i=1, ...
;; Θα δείτε παρακάτω πως να προσθέσουμε μια νέα μορφή επανάληψης
;; αλλά η Racket έχει ήδη πολύ ευέλικτη μορφή για τους βρόχους
(for ([i 10])
(printf "i=~a\n" i)) ; => i=0, i=1, ...
(for ([i (in-range 5 10)])
(printf "i=~a\n" i)) ; => i=5, i=6, ...
;;;
;;; Επανάληψη μέσα σε ακολουθίες:
;; Το `for' επιτρέπει την επενάληψη μέσα σε πολλά
;; άλλα ήδη από ακολουθίες: Λίστες, διανύσματα,
;; αλφαριθμητικά, σύνολα κτλ..
;;allows iteration over many other kinds of sequences:
;; lists, vectors, strings, sets, hash tables, etc...
(for ([i (in-list '(l i s t))])
(displayln i))
(for ([i (in-vector #(v e c t o r))])
(displayln i))
(for ([i (in-string "string")])
(displayln i))
(for ([i (in-set (set 'x 'y 'z))])
(displayln i))
(for ([(k v) (in-hash (hash 'a 1 'b 2 'c 3 ))])
(printf "key:~a value:~a\n" k v))
;;; Πιο περίπλοκες επαναλήψεις
;; Παράλληλη σάρωση σε πολλαπλές ακολουθίες
;; (σταματά στην πιο σύντομη)
(for ([i 10] [j '(x y z)]) (printf "~a:~a\n" i j))
; => 0:x 1:y 2:z
;; Εμφολευμένοι βρόχοι
(for* ([i 2] [j '(x y z)]) (printf "~a:~a\n" i j))
; => 0:x, 0:y, 0:z, 1:x, 1:y, 1:z
;; Συνθήκες
(for ([i 1000]
#:when (> i 5)
#:unless (odd? i)
#:break (> i 10))
(printf "i=~a\n" i))
; => i=6, i=8, i=10
;;; Σάρωση σε λίστες
;; Παρόμοιο με τους βρόχους 'for', απλά συλλέγουμε τα αποτελέσματα
(for/list ([i '(1 2 3)])
(add1 i)) ; => '(2 3 4)
(for/list ([i '(1 2 3)] #:when (even? i))
i) ; => '(2)
(for/list ([i 10] [j '(x y z)])
(list i j)) ; => '((0 x) (1 y) (2 z))
(for/list ([i 1000] #:when (> i 5) #:unless (odd? i) #:break (> i 10))
i) ; => '(6 8 10)
(for/hash ([i '(1 2 3)])
(values i (number->string i)))
; => '#hash((1 . "1") (2 . "2") (3 . "3"))
;; Υπάρχουν πολλά είδη απο προϋπάρχοντες τρόπους για να συλλέγουμε
;; τιμές από τους βρόχους
(for/sum ([i 10]) (* i i)) ; => 285
(for/product ([i (in-range 1 11)]) (* i i)) ; => 13168189440000
(for/and ([i 10] [j (in-range 10 20)]) (< i j)) ; => #t
(for/or ([i 10] [j (in-range 0 20 2)]) (= i j)) ; => #t
;; Και για να χρησιμοποιήσουμε ένα αφθαίρετο συνδιασμό χρησιμοποιύμε
;; το 'for/fold'
(for/fold ([sum 0]) ([i '(1 2 3 4)]) (+ sum i)) ; => 10
;; Αυτό συχνά μπορεί να αντικαταστήσει τους κοινούς
;; προστακτικούς βρόχους (imperative loops)
;;; Εξαιρέσεις
;; Για να πιάσουμε τις εξαιρέσεις χρησιμοποιούμε το
;; `with-handlers'
(with-handlers ([exn:fail? (lambda (exn) 999)])
(+ 1 "2")) ; => 999
(with-handlers ([exn:break? (lambda (exn) "no time")])
(sleep 3)
"phew") ; => "phew", αλλά αν γίνει το break => "no time"
;; Χρησιμοποιούμε το 'raise' για να άρουμε μια εξαίρεση
;; ή οποιαδήποτε άλλη τιμή
(with-handlers ([number? ; πιάνουμε αριθμητικές τιμές
identity]) ; και τις επιστρέφουμε σαν απλές τιμές
(+ 1 (raise 2))) ; => 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 6. Αλλαγή τιμών
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Χρησιμοποιούμε το 'set!' για να θέσουμε μια νέα τιμή
;; σε μια ήδη υπάρχουσα μεταβλητή
(define n 5)
(set! n (add1 n))
n ; => 6
;; Χρησιμοποιούμε τα boxes για να δηλώσουμε ρητά ότι μια μεταβητή
;; θα είναι mutable (θα μπορεί να αλλάξη η τιμή της)
;; Αυτό είναι παρόμοιο με τους pointers σε άλλες γλώσσες
(define n* (box 5))
(set-box! n* (add1 (unbox n*)))
(unbox n*) ; => 6
;; Πολλοί τύποι μεταβλητών στη Racket είναι αμετάβλητοι πχ τα ζεύγη, οι
;; λίστες κτλ. Άλλοι υπάρχουν και σε μεταβλητή και σε αμετάβλητη μορφή
;; πχ αλφαριθμητικά, διανύσματα κτλ
(define vec (vector 2 2 3 4))
(define wall (make-vector 100 'bottle-of-beer))
;; Χρησιμοποιούμε το 'vector-set!' για να ανεώσουμε κάποια
;; συγκεκριμένη θέση
(vector-set! vec 0 1)
(vector-set! wall 99 'down)
vec ; => #(1 2 3 4)
;; Έτσι δημιουργούμε ένα άδειο μεταβλητό πίνακα κατακερματισμού
;; και τον χειριζόμαστε κατάλληλα
(define m3 (make-hash))
(hash-set! m3 'a 1)
(hash-set! m3 'b 2)
(hash-set! m3 'c 3)
(hash-ref m3 'a) ; => 1
(hash-ref m3 'd 0) ; => 0
(hash-remove! m3 'a)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 7. Ενότητες (modules)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Οι ενότητες μας επιτρέπουν να οργανώνουμε τον κώδικα σε πολλαπλά
;; αρχεία και επαναχρησιμοποιούμενες βιβλιοθήκες
;; Εδώ χρησιμοποιούμε υπο-ενότητες, εμφωλευμένες μέσα σε μια
;; άλλη ενότητα που δημιουργεί αυτό το κείμενο(ξεκινώντας από
;; την γραμμή '#lang' )
(module cake racket/base ; ορίζουμε μια ενότητα 'cake' βασισμένο στο
; racket/base
(provide print-cake) ; συνάρτηση που εξάγεται από την ενότητα
(define (print-cake n)
(show " ~a " n #\.)
(show " .-~a-. " n #\|)
(show " | ~a | " n #\space)
(show "---~a---" n #\-))
(define (show fmt n ch) ; εσωτερική συνάρτηση
(printf fmt (make-string n ch))
(newline)))
;; Χρησιμοποιομε το 'require' για να πάρουμε όλα τα
;; παρεχόμενα ονόματα από μία ενότητα
(require 'cake) ; το ' είναι για τοπική υποενότητα
(print-cake 3)
; (show "~a" 1 #\A) ; => error, το `show' δεν έχει εξαχθεί
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 8. Κλάσεις και αντικείμενα
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Δημιουργούμε μια κλάση fish% (- συνήθως χρησιμοποιούμε
;; το % στο όνομα μιας κλάσης )
(define fish%
(class object%
(init size) ; initialization argument
(super-new) ; superclass initialization
;; Field
(define current-size size)
;; Public methods
(define/public (get-size)
current-size)
(define/public (grow amt)
(set! current-size (+ amt current-size)))
(define/public (eat other-fish)
(grow (send other-fish get-size)))))
;; Δημιουργούμε ένα instance του fish%
(define charlie
(new fish% [size 10]))
;; Χρησιμοποιούμε το 'send' για να καλέσουμε
;; τις μεθόδους ενός αντικειμένου
(send charlie get-size) ; => 10
(send charlie grow 6)
(send charlie get-size) ; => 16
;; Το `fish%' είναι μία τιμή "πρώτης κλάσης"
;; `fish%' is a plain "first class" value, με το οποίο μπορούμε να
;; κάνουμε προσμείξεις
(define (add-color c%)
(class c%
(init color)
(super-new)
(define my-color color)
(define/public (get-color) my-color)))
(define colored-fish% (add-color fish%))
(define charlie2 (new colored-fish% [size 10] [color 'red]))
(send charlie2 get-color)
;; ή χωρίς καθόλου ονόματα :
(send (new (add-color fish%) [size 10] [color 'red]) get-color)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 9. Μακροεντολές
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Οι μακροεντολές μας επιτρέπουν να επεκτείνουμε
;; το συντακτικό μιάς γλώσσας.
;; Ας προσθέσουμε έναν βρόχο while
(define-syntax-rule (while condition body ...)
(let loop ()
(when condition
body ...
(loop))))
(let ([i 0])
(while (< i 10)
(displayln i)
(set! i (add1 i))))
;; Macros are hygienic, you cannot clobber existing variables!
(define-syntax-rule (swap! x y) ; -! is idiomatic for mutation
(let ([tmp x])
(set! x y)
(set! y tmp)))
(define tmp 2)
(define other 3)
(swap! tmp other)
(printf "tmp = ~a; other = ~a\n" tmp other)
;; Η μεταβλητή 'tmp' μετονομάζεται σε 'tmp_1'
;; για να αποφευχθεί η σύγκρουση με τα ονόματα
;; (let ([tmp_1 tmp])
;; (set! tmp other)
;; (set! other tmp_1))
;; But they are still code transformations, for example:
(define-syntax-rule (bad-while condition body ...)
(when condition
body ...
(bad-while condition body ...)))
;; αυτή η μακροεντολή είναι χαλασένη: δημιουγεί ατέρμονα βρόχο
;; και αν προσπαθήσουμε να το χρησιμοποιήσουμε, ο μεταγλωττιστης
;; θα μπεί στον ατέρμονα βρόχο.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 10. Συμβόλαια
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Τα συμβόλαια βάζουν περιορισμόυς σε τιμές που προέρχονται
;; από ενότητες (modules)
(module bank-account racket
(provide (contract-out
[deposit (-> positive? any)] ; οι ποσότητες είναι πάντα θετικές
[balance (-> positive?)]))
(define amount 0)
(define (deposit a) (set! amount (+ amount a)))
(define (balance) amount)
)
(require 'bank-account)
(deposit 5)
(balance) ; => 5
;; Πελάτες που προσπαθούν να καταθέσουν ένα μη θετικό ποσό παίρνουν
;; το μήνυμα (deposit -5) ; => deposit: contract violation
;; expected: positive?
;; given: -5
;; more details....
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 11. Είσοδος και έξοδος
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Η Racket έχει την έννοια του "port", που είναι παρόμοιο με τα
;; file descriptors σε άλλες γλώσσες.
;; Ανοίγουμε το "/tmp/tmp.txt" και γράφουμε μέσα "Hello World"
;; Αυτό θα προκαλούσε σφάλμα αν το αρχείο υπήρχε ήδη
(define out-port (open-output-file "/tmp/tmp.txt"))
(displayln "Hello World" out-port)
(close-output-port out-port)
;; Προσθέτουμε στο τέλος του "/tmp/tmp.txt"
(define out-port (open-output-file "/tmp/tmp.txt"
#:exists 'append))
(displayln "Hola mundo" out-port)
(close-output-port out-port)
;; Διαβάζουμε απο αρχείο ξανά
(define in-port (open-input-file "/tmp/tmp.txt"))
(displayln (read-line in-port))
; => "Hello World"
(displayln (read-line in-port))
; => "Hola mundo"
(close-input-port in-port)
;; Εναλλακτικά, με το call-with-output-file δεν χρειάζεται να κλείσουμε
;; ρητά το αρχείο
(call-with-output-file "/tmp/tmp.txt"
#:exists 'update ; Rewrite the content
(λ (out-port)
(displayln "World Hello!" out-port)))
;; Και το call-with-input-file κάνει το ίδιο πράγμα για την είσοδο
(call-with-input-file "/tmp/tmp.txt"
(λ (in-port)
(displayln (read-line in-port))))
```
## Επιπλέον πηγές
Ψάχνεις για περισσότερα ; [Getting Started with Racket](http://docs.racket-lang.org/getting-started/)

View File

@@ -0,0 +1,690 @@
---
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"
// Expressions inside interpolated strings are also possible
// Εκφράσεις μέσα σε παρεμβεβλημένα αλφαριθμητικά είναι επίσης εφικτά
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)