diff options
Diffstat (limited to 'el-gr/haskell-gr.html.markdown')
-rw-r--r-- | el-gr/haskell-gr.html.markdown | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/el-gr/haskell-gr.html.markdown b/el-gr/haskell-gr.html.markdown new file mode 100644 index 00000000..d2fed85a --- /dev/null +++ b/el-gr/haskell-gr.html.markdown @@ -0,0 +1,476 @@ +--- +language: Haskell +filename: haskell-gr.html.markdown +contributors: + - ["Miltiadis Stouras", "https://github.com/mstou"] +--- + +Η 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. Περισσότερες συναρτήσεις +---------------------------------------------------- + +-- Μερική κλήση: αν δεν περάσουμε όλες τις μεταβλητές σε μια συνάρτηση, +-- τότε αυτή "καλείται μερικώς". Αυτό σημαίνει ότι μας επιστρέφει μια συνάρτηση +-- η οποία παίρνει ως ορίσματα τις εναπομείνασες μεταβλητές + +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/) |