learnxinyminutes-docs/el/racket.md
2024-12-08 23:20:53 -07:00

744 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
filename: learnracket-gr.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/"]
---
H Racket είναι μια γενικού σκοπού, πολυ-υποδειγματική γλώσσα προγραμματισμού που ανήκει
στην οικογένεια της Lisp/Scheme
```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. Μεταβλητές
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Μπορούμε να δημιουργήσουμε μεταβλητές
;; χρησιμοποιώντας το define.
;; Ένα όνομα μεταβλητής μπορεί να χρησιμοποιεί οποιονδήποτε
;; χαρακτήρα, εκτός από τους: ()[]{}",'`;#|\
(define some-var 5)
some-var ; => 5
;; Μπορούμε επίσης να χρησιμοποιήσουμε unicode χαρακτήρες.
(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
;;; Πίνακες κατακερματισμού (Hashes)
;; Δημιουργήστε ένα αμετάβλητο πίνακα κατακερματισμού
(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?' υποστηρίζει την σύγκριση των παρακάτω τύπων μεταβλητών:
;; αλφαριθμητικά, αλφαριθμητικά από 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' επιτρέπει την επανάληψη μέσα σε πολλά
;; άλλα ήδη από ακολουθίες: Λίστες, διανύσματα,
;; αλφαριθμητικά, σύνολα κτλ..
(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%' είναι μία τιμή "πρώτης κλάσης"
;; με το οποίο μπορούμε να κάνουμε προσμείξεις
(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))
;; Αλλά ακόμα υπάρχουν ακόμη μετασχηματισμοί του κώδικα, π.χ.:
(define-syntax-rule (bad-while condition body ...)
(when condition
body ...
(bad-while condition body ...)))
;; αυτή η μακροεντολή είναι χαλασμένη: δημιουργεί ατέρμονα βρόχο
;; και αν προσπαθήσουμε να το χρησιμοποιήσουμε, ο μεταγλωττιστής
;; θα μπει στον ατέρμονα βρόχο.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 10. Συμβόλαια (Contracts)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Τα συμβόλαια βάζουν περιορισμούς σε τιμές που προέρχονται
;; από ενότητες (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/)