2019-10-31 16:43:05 +00:00
|
|
|
|
---
|
|
|
|
|
contributors:
|
|
|
|
|
- ["Miltiadis Stouras", "https://github.com/mstou"]
|
2024-04-06 13:59:13 +00:00
|
|
|
|
filename: learnhaskell-gr.hs
|
2019-10-31 16:43:05 +00:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
Η Haskell σχεδιάστηκε για να είναι μια πρακτική, αγνή συναρτησιακή γλώσσα προγραμματισμού.
|
|
|
|
|
Είναι διάσημη για τα monads και το σύστημα τύπων της, αλλά χρησιμοποιείται από πολλούς
|
|
|
|
|
κυρίως για την κομψότητά της. Προσωπικά θεωρώ ότι είναι από τις πιο όμορφες, αν όχι
|
|
|
|
|
η πιο όμορφη, γλώσσα προγραμματισμού.
|
|
|
|
|
|
|
|
|
|
```haskell
|
|
|
|
|
-- Τα σχόλια μιας γραμμής ξεκινούν με 2 παύλες.
|
|
|
|
|
{- Ενώ τα σχόλια πολλών γραμμών βρίσκονται
|
|
|
|
|
μέσα σε blocks σαν αυτό
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- 1. Πρωτόγονοι Τύποι Δεδομένων (Primitive datatype) και Τελεστές
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- Οι αριθμοί είναι ένα primitive datatype
|
|
|
|
|
3 -- 3
|
|
|
|
|
|
|
|
|
|
-- Και οι τελεστές κάνουν αυτό που θα περιμέναμε
|
|
|
|
|
1 + 1 -- 2
|
|
|
|
|
8 - 1 -- 7
|
|
|
|
|
10 * 2 -- 20
|
|
|
|
|
35 / 5 -- 7.0
|
|
|
|
|
|
|
|
|
|
-- Η καθιερωμένη διαίρεση δεν είναι ακέραια
|
|
|
|
|
35 / 4 -- 8.75
|
|
|
|
|
|
|
|
|
|
-- Η ακέραια διαίρεση γίνεται με την συνάρτηση div
|
|
|
|
|
35 `div` 4 -- 8
|
|
|
|
|
|
|
|
|
|
-- Και οι boolean μεταβλητές ειναι primitives
|
|
|
|
|
True
|
|
|
|
|
False
|
|
|
|
|
|
|
|
|
|
-- Πράξεις με booleans
|
|
|
|
|
not True -- False
|
|
|
|
|
not False -- True
|
|
|
|
|
1 == 1 -- True
|
|
|
|
|
1 /= 1 -- False
|
|
|
|
|
1 < 10 -- True
|
|
|
|
|
|
|
|
|
|
-- Στα παραπάνω παραδείγματα, το `not` είναι μια συνάρτηση που παίρνει ένα όρισμα
|
|
|
|
|
-- Στην Haskell δεν χρειάζονται παρενθέσεις για τις κλήσεις συναρτήσεων, όλες οι παράμετροι
|
|
|
|
|
-- γράφονται με κενά αμέσως μετά την συνάρτηση. Στην γενική περίπτωση,
|
|
|
|
|
-- η κλήση συνάρτησης μοιάζει κάπως έτσι: func arg1 arg2 arg3...
|
|
|
|
|
-- Για το πως να ορίσετε τις δικές σας συναρτήσεις διαβάστε το κεφάλαιο των συναρτήσεων παρακάτω
|
|
|
|
|
|
|
|
|
|
-- Συμβολοσειρές και χαρακτήρες
|
|
|
|
|
"This is a string." -- συμβολοσειρά
|
|
|
|
|
'a' -- χαρακτήρας
|
|
|
|
|
'You cant use single quotes for strings.' -- error!
|
|
|
|
|
-- δεν μπορούμε να γράψουμε συμβολοσειρές ανάμεσα από ''
|
|
|
|
|
|
|
|
|
|
-- Οι συμβολοσειρές μπορούν να συννενωθούν με την χρήση του τελεστή ++
|
|
|
|
|
"Hello " ++ "world!" -- "Hello world!"
|
|
|
|
|
|
|
|
|
|
-- Η συμβολοσειρά είναι ουσιαστικά μια λίστα χαρακτήρων
|
|
|
|
|
['H', 'e', 'l', 'l', 'o'] -- "Hello"
|
|
|
|
|
"This is a string" !! 0 -- 'T'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- 2. Λίστες και διατεταγμένα σύνολα (tuples)
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- Όλα τα στοιχεία μιας λίστας πρέπει να είναι του ίδιου τύπου
|
|
|
|
|
-- Οι δύο παρακάτω λίστες είναι οι ίδιες:
|
|
|
|
|
[1, 2, 3, 4, 5]
|
|
|
|
|
[1..5] -- διάστημα ή range
|
|
|
|
|
|
|
|
|
|
-- Τα διαστήματα μπορούν να χρησιμοποιηθούν και για άλλους τύπους εκτός από αριθμούς
|
|
|
|
|
['A'..'F'] -- "ABCDEF"
|
|
|
|
|
|
|
|
|
|
-- Μπορούμε ακόμη να ορίσουμε και ένα βήμα
|
|
|
|
|
[0,2..10] -- [0, 2, 4, 6, 8, 10]
|
|
|
|
|
[5..1] -- [] (Το default βήμα της Haskell είναι το 1, επομένως η διπλανή λίστα είναι κενή)
|
|
|
|
|
[5,4..1] -- [5, 4, 3, 2, 1]
|
|
|
|
|
|
|
|
|
|
-- Προσπέλαση στοιχείου σε τυχαία θέση
|
|
|
|
|
[1..10] !! 3 -- 4 (οι δείκτες των θέσεων ξεκινούν από το 0)
|
|
|
|
|
|
|
|
|
|
-- Στην Haskell υπάρχουν και άπειρες λίστες!
|
|
|
|
|
[1..] -- η λίστα των φυσικών αριθμών
|
|
|
|
|
|
|
|
|
|
-- Οι άπειρες λίστες μπορούν να λειτουργούν επειδή η Haksell έχει "lazy evaluation".
|
|
|
|
|
-- Αυτό σημαίνει ότι η Haskell κάνει υπολογισμούς μόνο όταν πραγματικά χρειάζεται!
|
|
|
|
|
-- οπότε αν ζητήσουμε το 1000στό στοιχείο μιας άπειρης λίστας θα μας το δώσει,
|
|
|
|
|
-- ξέρει ότι δεν χρειάζεται να υπολογίσει όλη την άπειρη λίστα πρώτα!
|
|
|
|
|
|
|
|
|
|
[1..] !! 999 -- 1000
|
|
|
|
|
|
|
|
|
|
-- Στο παραπάνω παράδειγμα η Haskell υπολόγισε τα στοιχεία 1 μέχρι 1000...τα υπόλοιπα
|
|
|
|
|
-- στοιχεία της άπειρης λίστας δεν υπάρχουν ακόμα! Η Haskell θα τα υπολογίσει
|
|
|
|
|
-- μόνο αν κάποια στιγμή τα χρειαστεί.
|
|
|
|
|
|
|
|
|
|
-- συνένωση δύο λιστών με τον τελεστή ++ (σε γραμμικό χρόνο)
|
|
|
|
|
[1..5] ++ [6..10]
|
|
|
|
|
|
|
|
|
|
-- προσθήκη στοιχείου στην αρχή της λίστας (σε σταθερό χρόνο)
|
|
|
|
|
0:[1..5] -- [0, 1, 2, 3, 4, 5]
|
|
|
|
|
|
|
|
|
|
-- περισσότερες συναρτήσεις για τις λίστες
|
|
|
|
|
head [1..5] -- 1
|
|
|
|
|
tail [1..5] -- [2, 3, 4, 5]
|
|
|
|
|
init [1..5] -- [1, 2, 3, 4]
|
|
|
|
|
last [1..5] -- 5
|
|
|
|
|
|
|
|
|
|
-- list comprehensions
|
|
|
|
|
-- ένας άλλος τρόπος να ορίζουμε τις λίστες που θυμίζει πολύ τον ορισμό συνόλων στα μαθηματικά!
|
|
|
|
|
[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10]
|
|
|
|
|
|
|
|
|
|
-- list comprehension με συνθήκη
|
|
|
|
|
[x*2 | x <- [1..5], x*2 > 4] -- [6, 8, 10]
|
|
|
|
|
|
|
|
|
|
-- Κάθε στοιχείο ενός tuple μπορεί να έχει διαφορετικό τύπο, όμως το tuple έχει σταθερό μέγεθος.
|
|
|
|
|
-- Ένα tuple:
|
|
|
|
|
("haskell", 1)
|
|
|
|
|
|
|
|
|
|
-- προσπέλαση στοιχείων ενός ζεύγους στοιχείων (δηλαδή ενός tuple μεγέθους 2)
|
|
|
|
|
fst ("haskell", 1) -- "haskell"
|
|
|
|
|
snd ("haskell", 1) -- 1
|
|
|
|
|
|
|
|
|
|
-- οι παραπάνω συναρτήσεις δεν λειτουργούν σε tuples μεγαλύτερου μεγέθους
|
|
|
|
|
snd ("snd", "can't touch this", "da na na na") -- error!
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- 3. Συναρτήσεις
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- Μια απλή συνάρτηση που παίρνει 2 μεταβλητές a, b και επιστρέφει το άθροισμά τους
|
|
|
|
|
add a b = a + b
|
|
|
|
|
|
|
|
|
|
-- Προσέξτε ότι αν χρησιμοποιείτε το διαδραστικό περιβάλλον της Haskell (ghci), δηλαδή
|
|
|
|
|
-- τον interpreter, θα πρέπει να προσθέσετε ενα `let` πριν τον ορισμό της συνάρτησης:
|
|
|
|
|
-- let add a b = a + b
|
|
|
|
|
|
|
|
|
|
-- Κλήση της συνάρτησης
|
|
|
|
|
add 1 2 -- 3
|
|
|
|
|
|
|
|
|
|
-- Μπορούμε να καλέσουμε την συνάρτηση και σαν τελεστή ανάμεσα στα 2 ορίσματα
|
|
|
|
|
-- γράφοντας το όνομα της συνάρτησης μέσα σε backticks:
|
|
|
|
|
1 `add` 2 -- 3
|
|
|
|
|
|
|
|
|
|
-- Μπορούμε να ορίσουμε και συναρτήσεις που δεν έχουν γράμματα στο όνομά τους!
|
|
|
|
|
-- Αυτό μας επιτρέπει να ορίσουμε δικούς μας τελεστές, όπως για παράδειγμα την ακέραια διάιρεση:
|
|
|
|
|
|
|
|
|
|
(//) a b = a `div` b
|
|
|
|
|
35 // 4 -- 8
|
|
|
|
|
|
|
|
|
|
-- Guards: ένας εύκολος τρόπος να υλοποιήσουμε διακλαδώσεις σε μια συνάρτηση
|
|
|
|
|
fib x
|
|
|
|
|
| x < 2 = 1
|
|
|
|
|
| otherwise = fib (x - 1) + fib (x - 2)
|
|
|
|
|
|
|
|
|
|
-- Το ταίριασμα προτύπων (Pattern matching) είναι παρόμοιο.
|
|
|
|
|
-- Εδώ δίνουμε 3 διαφορετικούς ορισμούς για την συνάρτηση fib
|
|
|
|
|
-- H Haskell θα χρησιμοποιήσει αυτόματα τον πρώτο ορισμό το οποίου οι παράμετροι
|
|
|
|
|
-- ταιριάζουν με τις παραμέτρους της κλήσης
|
|
|
|
|
|
|
|
|
|
fib 1 = 1
|
|
|
|
|
fib 2 = 2
|
|
|
|
|
fib x = fib (x - 1) + fib (x - 2)
|
|
|
|
|
|
|
|
|
|
-- Pattern matching σε tuples
|
|
|
|
|
sndOfTriple (_, y, _) = y
|
|
|
|
|
-- η κάτω παύλα χρησιμοποιείται για να μην δίνουμε ονόματα
|
|
|
|
|
-- σε μεταβλητές που δεν θα χρησιμοποιήσουμε και
|
|
|
|
|
-- ταιριάζει με όλους τους τύπους
|
|
|
|
|
|
|
|
|
|
-- Pattern matching σε λίστες.
|
|
|
|
|
-- Στο παρακάτω παράδειγμα, το `x` είναι το πρώτο στοιχείο της λίστας
|
|
|
|
|
-- και τo `xs` είναι η λίστα με τα υπόλοιπα στοιχεία
|
|
|
|
|
|
|
|
|
|
myMap func [] = []
|
|
|
|
|
myMap func (x:xs) = func x : (myMap func xs)
|
|
|
|
|
|
|
|
|
|
-- Μπορούμε να ορίσουμε και ανώνυμες συναρτήσεις (lambdas) χρησιμοποιώντας το
|
|
|
|
|
-- backslash (που μοιάζει με λ) ακολουθούμενο από τις παραμέτρους:
|
|
|
|
|
myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7]
|
|
|
|
|
|
|
|
|
|
-- χρήση της συνάρτησης fold με μία ανώνυμη συνάρτηση
|
|
|
|
|
-- Το foldl1 είναι σαν fold από αριστερά, αλλά χρησιμοποιεί σαν αρχική τιμή του
|
|
|
|
|
-- accumulator το πρώτο στοιχείο της λίστας.
|
|
|
|
|
foldl1 (\acc x -> acc + x) [1..5] -- 15
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- 4. Περισσότερες συναρτήσεις
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- Μερική κλήση: αν δεν περάσουμε όλες τις μεταβλητές σε μια συνάρτηση,
|
|
|
|
|
-- τότε αυτή "καλείται μερικώς". Αυτό σημαίνει ότι μας επιστρέφει μια συνάρτηση
|
2024-04-06 10:21:13 +00:00
|
|
|
|
-- η οποία παίρνει ως ορίσματα τις εναπομείνασες μεταβλητές
|
2019-10-31 16:43:05 +00:00
|
|
|
|
|
|
|
|
|
add a b = a + b
|
|
|
|
|
foo = add 10 -- η foo είναι μια συνάρτηση που περιμένει 1 αριθμό και του προσθέτει 10
|
|
|
|
|
foo 5 -- 15
|
|
|
|
|
|
|
|
|
|
-- Ένας άλλος τρόπος να γράψουμε το ίδιο πράγμα:
|
|
|
|
|
foo = (10+)
|
|
|
|
|
foo 5 -- 15
|
|
|
|
|
|
|
|
|
|
-- Σύνθεση συναρτήσεων
|
|
|
|
|
-- Ο τελεστής `.` χρησιμοποιείται για την σύνθεση ("αλυσίδωση") συναρτήσεων.
|
|
|
|
|
-- Για παράδειγμα, η foo παρακάτω είναι μια συνάρτηση που παίρνει ως όρισμα 1 αριθμό.
|
|
|
|
|
-- Πρώτα προσθέτει 10 στον αριθμό που δώσαμε και μετά πολλαπλασιάζει το αποτέλεσμα με 4
|
|
|
|
|
foo = (4*) . (10+)
|
|
|
|
|
|
|
|
|
|
-- 4*(10+5) = 60
|
|
|
|
|
foo 5 -- 60
|
|
|
|
|
|
|
|
|
|
-- διόρθωση προτεραιότητας
|
|
|
|
|
-- Στην Haskell υπάρχει ο τελεστής `$`. Ο τελεστής αυτός εφαρμόζει μια συνάρτηση
|
|
|
|
|
-- σε μία παράμετρο. Σε αντίθεση με την απλή εφαρμογή συνάρτησης, η οποία έχει
|
|
|
|
|
-- την μεγαλύτερη πιθανή προτεραιότητα και είναι αριστερά προσεταιριστική,
|
|
|
|
|
-- ο τελεστής `$` έχει την ελάχιστη προτεραιότητας και είναι δεξιά προσεταιριστικός.
|
|
|
|
|
-- Λόγω της χαμηλής του προτεραιότητας, η έκφραση που βρίσκεται στα δεξιά του
|
|
|
|
|
-- θα υπολογιστεί και θα περαστεί σαν παράμετρος στην συνάρτηση που βρίσκεται στα αριστερά του
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- πριν
|
|
|
|
|
even (fib 7) -- false
|
|
|
|
|
|
|
|
|
|
-- ισοδύναμα
|
|
|
|
|
even $ fib 7 -- false
|
|
|
|
|
|
|
|
|
|
-- χρησιμοποιόντας σύνθεση συναρτήσεων
|
|
|
|
|
even . fib $ 7 -- false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- 5. Τύποι
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- Η Haskell έχει ένα πολύ ισχυρό σύστημα τύπων, στο οποίο κάθε έκφραση έχει έναν τύπο
|
|
|
|
|
|
|
|
|
|
-- Κάποιο βασικοί τύποι:
|
|
|
|
|
5 :: Integer
|
|
|
|
|
"hello" :: String
|
|
|
|
|
True :: Bool
|
|
|
|
|
|
|
|
|
|
-- Και οι συναρτήσεις έχουν κάποιο τύπο
|
|
|
|
|
-- Η συνάρτηση`not` παίρνει ένα boolean και επιστρέφει ένα boolean:
|
|
|
|
|
-- not :: Bool -> Bool
|
|
|
|
|
|
|
|
|
|
-- Παρακάτω βλέπετε μια συνάρτηση που παίρνει 2 ορίσματα:
|
|
|
|
|
-- add :: Integer -> Integer -> Integer
|
|
|
|
|
|
|
|
|
|
-- Όταν ορίζουμε μια συνάρτηση ή μεταβλητή, είναι καλή πρακτική να γράφουμε
|
|
|
|
|
-- και τον τύπο της:
|
|
|
|
|
double :: Integer -> Integer
|
|
|
|
|
double x = x * 2
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- 6. Έλεγχος ροής και συνθήκες
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- if-expressions
|
|
|
|
|
haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome"
|
|
|
|
|
|
|
|
|
|
-- τα if-expressions μπορούν να πιάνουν και πολλές γραμμές
|
|
|
|
|
-- αλλά η στοίχιση είναι σημαντική!
|
|
|
|
|
haskell = if 1 == 1
|
|
|
|
|
then "awesome"
|
|
|
|
|
else "awful"
|
|
|
|
|
|
|
|
|
|
-- case expressions: Με τον παρακάτω τρόπο θα μπορούσαμε να κάνουμε parse
|
|
|
|
|
-- command line arguments
|
|
|
|
|
case args of
|
|
|
|
|
"help" -> printHelp
|
|
|
|
|
"start" -> startProgram
|
|
|
|
|
_ -> putStrLn "bad args"
|
|
|
|
|
|
|
|
|
|
-- Η Haskell δεν έχει βρόχους επανάληψης; αντιθέτως, χρησιμοποιούμε αναδρομή.
|
|
|
|
|
-- Η συνάρτηση map εφαρμόζει μια συνάρτηση σε κάθε στοιχείο μιας λίστας
|
|
|
|
|
|
|
|
|
|
map (*2) [1..5] -- [2, 4, 6, 8, 10]
|
|
|
|
|
|
|
|
|
|
-- μπορούμε να κατασκευάσουμε τον βρόχο for χρησιμοποιώντας την map
|
|
|
|
|
for array func = map func array
|
|
|
|
|
|
|
|
|
|
-- και να τον χρησιμοποιήσουμε
|
|
|
|
|
for [0..5] $ \i -> show i
|
|
|
|
|
|
|
|
|
|
-- το παραπάνω θα μπορούσε να γραφτεί και έτσι:
|
|
|
|
|
for [0..5] show
|
|
|
|
|
|
|
|
|
|
-- Μπορούμε να χρησιμοποιήσουμε τις συναρτήσεις foldl και foldr
|
|
|
|
|
-- για να υπολογίζουμε μια τιμή από μια λίστα (πχ άθροισμα ή γινόμενο)
|
|
|
|
|
-- foldl <fn> <initial value> <list>
|
|
|
|
|
foldl (\x y -> 2*x + y) 4 [1,2,3] -- 43
|
|
|
|
|
|
|
|
|
|
-- Η παραπάνω κλήση είναι η ίδια με:
|
|
|
|
|
(2 * (2 * (2 * 4 + 1) + 2) + 3)
|
|
|
|
|
|
|
|
|
|
-- Η foldl γίνεται από τα αριστερά ενώ η foldr από τα δεξιά
|
|
|
|
|
foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16
|
|
|
|
|
|
|
|
|
|
-- Η παραπάνω κλήση είναι τώρ:
|
|
|
|
|
(2 * 1 + (2 * 2 + (2 * 3 + 4)))
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- 7. Τύποι δεδομένων
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- Με τον παρακάτω τρόπο μπορούμε να ορίζουμε δικούς μας τύπους
|
|
|
|
|
-- δεδομένων στην Haskell
|
|
|
|
|
|
|
|
|
|
data Color = Red | Blue | Green
|
|
|
|
|
|
|
|
|
|
-- Τώρα μπορούμε να χρησιμοποιήσουμε τον τύπο μας και σε συναρτήσεις:
|
|
|
|
|
|
|
|
|
|
say :: Color -> String
|
|
|
|
|
say Red = "You are Red!"
|
|
|
|
|
say Blue = "You are Blue!"
|
|
|
|
|
say Green = "You are Green!"
|
|
|
|
|
|
|
|
|
|
-- Οι τύποι δεδομένων μας μπορεί να είναι και παραμετρικοί, να δέχονται δηλαδή
|
|
|
|
|
-- κάποιον τύπο ως παράμετρο
|
|
|
|
|
|
|
|
|
|
data Maybe a = Nothing | Just a
|
|
|
|
|
|
|
|
|
|
-- Όλες οι παρακάτω τιμές έχουν τύπο Maybe
|
|
|
|
|
Just "hello" -- of type `Maybe String`
|
|
|
|
|
Just 1 -- of type `Maybe Int`
|
|
|
|
|
Nothing -- of type `Maybe a` for any `a`
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- 8. Haskell IO
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- Αν και το IO δεν μπορεί να εξηγηθεί σε βάθος χωρίς να εξηγήσουμε
|
|
|
|
|
-- πρώτα τα monads, δεν είναι δύσκολο να το εξηγήσουμε αρκετά ώστε να μπορεί
|
|
|
|
|
-- κάποιος να το χρησιμοποιήσει
|
|
|
|
|
|
|
|
|
|
-- Όταν ένα πρόγραμμα Haskell εκτελείται, καλείται η συνάρτηση `main`
|
|
|
|
|
-- Η συνάρτηση αυτή πρέπει να επιστρέφει τύπο `IO a` για κάποιο τύπο `a`.
|
|
|
|
|
-- Για παράδειγμα:
|
|
|
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
|
main = putStrLn $ "Hello, sky! " ++ (say Blue)
|
|
|
|
|
-- η συνάρτηση putStrLn έχει τύπο: String -> IO ()
|
|
|
|
|
|
|
|
|
|
-- Είναι πιο εύκολο να χρησιμοποιήσουμε IO αν μπορούμε να γράψουμε το πρόγραμμά μας
|
|
|
|
|
-- ως μια συνάρτηση από String σε String. Η συνάρτηση
|
|
|
|
|
-- interact :: (String -> String) -> IO ()
|
|
|
|
|
-- παίρνει ως είσοδο ένα string, τρέχει μια συνάρτηση πάνω στην είσοδο
|
|
|
|
|
-- και τυπώνει την έξοδο
|
|
|
|
|
|
|
|
|
|
countLines :: String -> String
|
|
|
|
|
countLines = show . length . lines
|
|
|
|
|
|
|
|
|
|
main' = interact countLines
|
|
|
|
|
|
|
|
|
|
-- Μπορείτε να σκεφτείτε μια συνάρτηση που επιστρέφει τιμή με τύπο `IO ()`
|
|
|
|
|
-- ως μια ακολουθία πράξεων, περίπου όπως και σε μια imperative γλώσσα
|
|
|
|
|
-- Μπορούμε να χρησιμοποιήσουμε το `do` και να ενώσουμε αυτές τις κλήσεις
|
|
|
|
|
-- Για παράδειγμα:
|
|
|
|
|
|
|
|
|
|
sayHello :: IO ()
|
|
|
|
|
sayHello = do
|
|
|
|
|
putStrLn "What is your name?"
|
|
|
|
|
name <- getLine -- η συνάρτηση αυτή διαβάζει μια γραμμή και την αναθέτει στην μετβαλήτη name
|
|
|
|
|
putStrLn $ "Hello, " ++ name
|
|
|
|
|
|
|
|
|
|
-- Δοκιμάστε να γράψετε την συνάρτηση `interact` που θα διαβάζει μια γραμμή
|
|
|
|
|
|
|
|
|
|
-- Ωστόσο ο κώδικας της συνάρτησης `sayHello` δεν θα εκτελεστεί ποτέ. Η μόνη συνάρτηση
|
|
|
|
|
-- που εκτελείται όταν κάνουμε compile ένα αρχείο haskell είναι η `main`.
|
|
|
|
|
-- Αν θέλετε να τρέξετε την sayHello (εκτός από το να φορτώσετε τον κώδικα στο
|
|
|
|
|
-- ghci) μπορείτε να βάλετε σε σχόλια τον προηγούμενο ορισμό της main
|
|
|
|
|
-- και να την ορίσετε ως:
|
|
|
|
|
-- main = sayHello
|
|
|
|
|
|
|
|
|
|
-- Ας προσπαθήσουμε να καταλάβουμε πως λειτουργεί η συνάρτηση `getLine`
|
|
|
|
|
-- Ο τύπος της είναι:
|
|
|
|
|
-- getLine :: IO String
|
|
|
|
|
-- Μπορείτε να φανταστείτε ότι μια τιμή με τύπο `IO a` θα παραχθεί
|
|
|
|
|
-- από ένα πρόγραμμα που παράγει μια τιμή με τύπο `a` (ενώ παράλληλα κάνει και κάτι άλλο)
|
|
|
|
|
-- Μπορούμε να πάρουμε και να επαναχρησιμοποιήσουμε αυτήν την τιμή χρησιμοποιώντας
|
|
|
|
|
-- το `<-`. Μπορούμε ακόμα και να φτιάξουμε την δική μας συνάρτηση με τύπο
|
|
|
|
|
-- `IO String`:
|
|
|
|
|
|
|
|
|
|
action :: IO String
|
|
|
|
|
action = do
|
|
|
|
|
putStrLn "This is a line. Duh"
|
|
|
|
|
input1 <- getLine
|
|
|
|
|
input2 <- getLine
|
|
|
|
|
-- Ο τύπος του `do` μπλοκ είναι εκείνος της τελευταίας γραμμής.
|
|
|
|
|
-- Το `return` δεν είναι κάποια ειδική λέξη, αλλά απλώς μια συνάρτηση
|
|
|
|
|
return (input1 ++ "\n" ++ input2) -- return :: String -> IO String
|
|
|
|
|
|
|
|
|
|
-- Μπορούμε να χρησιμοποιήσουμε την παραπάνω συνάρτηση ακριβώς όπως την `getLine`:
|
|
|
|
|
|
|
|
|
|
main'' = do
|
|
|
|
|
putStrLn "I will echo two lines!"
|
|
|
|
|
result <- action
|
|
|
|
|
putStrLn result
|
|
|
|
|
putStrLn "This was all, folks!"
|
|
|
|
|
|
|
|
|
|
-- Ο τύπος `IO` είναι παράδειγμα ενός "monad". Χρησιμοποιώντας τα monads για το
|
|
|
|
|
-- ΙΟ, η Haskell καταφέρνει να είναι αγνή συναρτησιακή γλώσσα. Κάθε συνάρτηση που
|
|
|
|
|
-- αλληλεπιδρά με τον έξω κόσμο (δηλαδή κάνει IO), έχει το IO (ή κάποιο άλλο monad)
|
|
|
|
|
-- στον τύπο της. Αυτό μας διευκολύνει να γνωρίζουμε ποιές συναρτήσεις είναι αγνές
|
|
|
|
|
-- (μαθηματικές -- δεν αλληλεπιδρούν με τον έξω κόσμο ούτε αλλάζουν κάποιο state)
|
|
|
|
|
-- και ποιες δεν είναι.
|
|
|
|
|
|
|
|
|
|
-- Αυτό είναι ένα πολύ ισχυρό χαρακτηριστικό γιατί είναι πολύ εύκολο να
|
|
|
|
|
-- εκτελούμε παράλληλα αγνές συναρτήσεις! Οπότε η παραλληλοποίηση στην Haskell
|
|
|
|
|
-- είναι αρκετά πιο εύκολη
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
-- 9. Haskell REPL
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
-- Μπορείτε να ξεκινήσετε το διαδραστικό περιβάλλον της Haskell με την εντολή `ghci`.
|
|
|
|
|
-- Εδώ μπορείτε να γράψετε και να εκτελέσετε κώδικα haskell.
|
|
|
|
|
-- Κάθε νέα τιμή πρέπει να ορίζεται με το `let`
|
|
|
|
|
|
|
|
|
|
let foo = 5
|
|
|
|
|
|
|
|
|
|
-- Μπορείτε να βρείτε τον τύπο μιας συνάρτησης με το `:t`:
|
|
|
|
|
|
|
|
|
|
> :t foo
|
|
|
|
|
foo :: Integer
|
|
|
|
|
|
|
|
|
|
-- Οι τελεστές, όπως οι `+`, `:` και `$`, είναι επίσης συναρτήσεις.
|
|
|
|
|
-- Μπορούμε να δούμε τον τύπο τους βάζοντας τους μέσα σε παρενθέσεις:
|
|
|
|
|
|
|
|
|
|
> :t (:)
|
|
|
|
|
(:) :: a -> [a] -> [a]
|
|
|
|
|
|
|
|
|
|
-- Για περισσότερες πληροφορίες για οποιαδήποτε συνάρτηση ή τύπο,
|
|
|
|
|
-- μπορείτε να χρησιμοποιήσετε το `:i`:
|
|
|
|
|
|
|
|
|
|
> :i (+)
|
|
|
|
|
class Num a where
|
|
|
|
|
(+) :: a -> a -> a
|
|
|
|
|
...
|
|
|
|
|
-- Defined in ‘GHC.Num’
|
|
|
|
|
infixl 6 +
|
|
|
|
|
|
|
|
|
|
-- Μπορείτε επίσης να τρέξετε κάθε συνάρτηση με τύπο `IO ()`
|
|
|
|
|
|
|
|
|
|
> sayHello
|
|
|
|
|
What is your name?
|
|
|
|
|
Friend!
|
|
|
|
|
Hello, Friend!
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Υπάρχουν πολλά ακόμα πράγματα να εξερευνήσετε στην Haskell, όπως τα typeclasses
|
|
|
|
|
και διάφορα monads! Αυτές οι μαθηματικά ορισμένες έννοιες είναι που κάνουν την
|
|
|
|
|
Haskell αυστηρή, αγνή και κομψή! Θα τελειώσουμε αυτήν την σύντομη περιήγηση με
|
|
|
|
|
ένα τελευταίο παράδειγμα, η υλοποίηση της QuickSort σε Haskell:
|
|
|
|
|
|
|
|
|
|
```haskell
|
|
|
|
|
qsort [] = []
|
|
|
|
|
qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
|
|
|
|
|
where lesser = filter (< p) xs
|
|
|
|
|
greater = filter (>= p) xs
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Υπάρχουν 2 παραδοσιακοί τρόποι να εγκαταστήσετε την Haskell:
|
|
|
|
|
- [Cabal-based installation](http://www.haskell.org/platform/),
|
|
|
|
|
- [Stack-based process](https://www.stackage.org/install).
|
|
|
|
|
|
|
|
|
|
Στις παρακάτω πηγές μπορείτε να βρείτε αρκετά κομψές εισαγωγές στην Haskell
|
|
|
|
|
- [Learn you a Haskell](http://learnyouahaskell.com/),
|
|
|
|
|
- [Happy Learn Haskell Tutorial](http://www.happylearnhaskelltutorial.com/),
|
|
|
|
|
- [Real World Haskell](http://book.realworldhaskell.org/)
|