ΠΑΝΕΠΙΣΤΗΜΙΟ ΙΩΑΝΝΙΝΩΝ ΤΜΗΜΑ ΜΗΧΑΝΙΚΩΝ Η/Υ ΚΑΙ ΠΛΗΡΟΦΟΡΙΚΗΣ ΜΑΘΗΜΑ: ΑΡΧΕΣ ΓΛΩΣΣΩΝ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ ΑΚΑΔ. ΕΤΟΣ: 2018-19 ΔΙΔΑΣΚΩΝ: Χ.ΝΟΜΙΚΟΣ 2η Σειρά Εργαστηριακών Ασκήσεων Οι εργαστηριακές ασκήσεις είναι ατομικές. Οι απαντήσεις θα πρέπει να υποβληθούν με turnin, το αργότερο μέχρι την Παρασκευή 29 Μαρτίου 2019. Για τη συγγραφή των προγραμμάτων επιτρέπεται να χρησιμοποιήσετε μόνο τις προκαθοριμένες συναρτήσεις και τους προκαθορισμένους τελεστές που αναφέρονται στις σημειώσεις του μαθήματος. Δεν επιτρέπεται η χρήση του import. Οι ώρες οι οποίες έχουν δεσμευτεί για το εργαστήριο του μαθήματος είναι την Τετάρτη 12-2μμ. Η παρουσία στο εργαστήριο τις παραπάνω ώρες δεν είναι υποχρεωτική. Μπορείτε να έρχεστε στο εργαστήριο τις ώρες αυτές για όποια βοήθεια χρειάζεστε σχετικά με την εκπόνηση των εργαστηριακών ασκήσεων και γενικότερα τον προγραμματισμό στη γλώσσα Haskell, καθώς και για την επίλυση προβλημάτων που παρουσιάζονται κατά τη συγγραφή των προγραμμάτων στο πλαίσιο των εργαστηριακών ασκήσεων. Σημειώνεται ότι σε καμία περίπτωση δεν θα παρέχεται σχετική βοήθεια μέσω e-mail. Για τη συγγραφή των συναρτήσεων συνίσταται να χρησιμοποιήσετε το αρχείο πρότυπο Lab2.hs (που υπάρχει στην ιστοσελίδα του μαθήματος), στο οποίο υπάρχουν έτοιμες οι δηλώσεις τύπων των συναρτήσεων που θα πρέπει να κατασκευάσετε καθώς και μία ισότητα που ορίζει τις συναρτήσεις ώστε να επιστρέφουν μία προκαθοριμένη τιμή για όλες τις τιμές των ορισμάτων. Για να απαντήσετε σε μία άσκηση μπορείτε να αντικαταστήσετε την παραπάνω ισότητα με τις κατάλληλες ισότητες που ορίζουν την τιμή της συνάρτησης. Δεν θα πρέπει να τροποποιήσετε το τύπο ούτε το όνομα της συνάρτησης. Μπορείτε να χρησιμοποιήσετε όσες βοηθητικές συναρτήσεις θέλετε, οι οποίες θα καλούνται από τις συναρτήσεις που σας ζητείται να υλοποιήσετε. Σε καμία περίπτωση δεν θα πρέπει να προσθέσετε άλλα ορίσματα στις συναρτήσεις που σας ζητούνται (καθώς αυτό συνεπάγεται αλλαγή του τύπου τους). Αν χρησιμοποιήσετε προκαθοριμένες συναρτήσεις ή τελεστές που δεν αναφέρονται στις σημειώσεις του μαθήματος ή αν χρησιμοποιήσετε το import για να ενσωματώσετε έτοιμο κώδικα, η αντίστοιχη άσκηση δεν θα βαθμολογηθεί.
Ο έλεγχος της ορθότητας των απαντήσεων θα γίνει με ημι-αυτόματο τρόπο. Σε καμία περίπτωση δεν θα πρέπει ο βαθμολογητής να χρειάζεται να κάνει παρεμβάσεις στο αρχείο που θα υποβάλετε. Συνεπώς θα πρέπει να λάβετε υπόψη τα παρακάτω: 1. Κάθε μία από τις συναρτήσεις που σας ζητείται να υλοποιήσετε θα πρέπει να έχει το συγκεκριμένο όνομα και το συγκεκριμένο τύπο που περιγράφεται στην εκφώνηση της αντίστοιχης άσκησης και που υπάρχει στο αρχείο πρότυπο Lab2.hs. Αν σε κάποια άσκηση το όνομα ή ο τύπος της συνάρτησης δεν συμφωνεί με αυτόν που δίνεται στην εκφώνηση, η άσκηση δεν θα βαθμολογηθεί. 2. Το αρχείο που θα παραδώσετε δεν θα πρέπει να περιέχει συντακτικά λάθη. Αν υπάρχουν τμήματα κώδικα που περιέχουν συντακτικά λάθη, τότε θα πρέπει να τα διορθώσετε ή να τα αφαιρέσεται πριν από την παράδοση. Αν το αρχείο που θα υποβάλετε περιέχει συντακτικά λάθη, τότε ολόκληρη η εργαστηριακή άσκηση θα μηδενιστεί. 3. Οι συναρτήσεις θα πρέπει να επιστρέφουν αποτέλεσμα για όλες τις τιμές των ορισμάτων που δίνονται για έλεγχο στο τέλος κάθε άσκησης. Αν κάποιες από τις τιμές που επιστρέφουν οι συναρτήσεις δεν είναι σωστές, αυτό θα ληφθεί υπόψη στη βαθμολογία, ωστόσο η άσκηση θα βαθμολογηθεί κανονικά. Αν ωστόσο οι συναρτήσεις δεν επιστρέφουν τιμές για κάποιες από τις τιμές ελέγχου (π.χ. προκαλούν υπερχείλιση στοίβας, ατέρμονο υπολογισμό ή κάποιο σφάλμα χρόνου εκτέλεσης) τότε η αντίστοιχη άσκηση δεν θα βαθμολογηθεί. 4. Κατα τη διόρθωση των ασκήσεων οι βαθμολογητές δεν θα κάνουν κλήσεις στις βοηθητικές συναρτήσεις που ενδεχομένως θα χρησιμοποιήσετε. Η χρήση των βοηθητικών συναρτήσεων θα πρέπει να γίνεται μέσα από τις συναρτήσεις που σας ζητείται να υλοποιήσετε. Μετά το τέλος της εκφώνησης κάθε άσκησης δίνονται τιμές που μπορείτε να χρησιμοποιήσετε για έλεγχο της ορθότητας των συναρτήσεων. Για υποβολή με turnin γράψτε (υποθέτοντας ότι έχετε γράψει το πρόγραμμα στο αρχείο Lab2.hs): turnin Haskell-2@myy401 Lab2.hs
Ασκηση 1. Γράψτε μία συνάρτηση xsum σε Haskell, η οποία θα δέχεται ως όρισμα μία λίστα ακεραίων αριθμών s = [x 1, x 2,..., x k ] και θα επστρέφει το άθροισμα k 1 (( i=1 i x j ) ( j=1 k j=i+1 x j )) = x 1 (x 2 + x 3 +... + x k ) +(x 1 + x 2 ) (x 3 + x 4 +... + x k ) +... +(x 1 + x 2 +... + x k 2 ) (x k 1 + x k ) +(x 1 + x 2 +... + x k 1 ) x k Αν η s έχει λιγότερα από 2 στοιχεία, τότε η xsum θα πρέπει να επιστρέφει την τιμή 0. Ο τύπος της συνάρτησης xsum θα πρέπει να είναι [Int]->Int. Main> xsum [4,5,8] 124 Main> xsum [10,3,12,7] 642 Main> xsum [10,7,4,8,12] 1486 Main> xsum [1..10] 4158 Main> xsum [1..100] 341665830
Ασκηση 2. Ενα ρομπότ μπορεί να κινείται σε ένα επίπεδο, το οποίο είναι χωρισμένο σε τετράγωνους τομείς. Κάθε τομέας περιγράφεται από ένα ζεύγος ακεραίων αριθμών που καθορίζει τις συντεταγμένες του. Το ρομπότ για να κινηθεί από έναν τομέα αφετηρία (x 1, y 1 ) σε έναν τομέα προορισμό (x 2, y 2 ), αρχικά κινείται διαγώνια προς την κατάλληλη κατεύθυνση, μέχρι κάποια από τις δύο συνιστώσες του να γίνει ίση με την αντίστοιχη του τομέα προορισμού. Στη συνέχεια, κινείται οριζόντια ή κάθετα μέχρι να φτάσει στον τομέα προορισμό. Για πάραδειγμα, για να κινηθεί από τον τομέα ( 2, 3) στον τομέα (4, 5), το ρομπότ θα περάσει από τους τομείς ( 1, 4), (0, 5), (1, 5), (2, 5) και (3, 5), ενώ για να κινηθεί από τον τομέα (3, 8) στον τομέα (9, 0), θα περάσει από τους τομείς (4, 7), (5, 6), (6, 5), (7, 4), (8, 3), (9, 2) και (9, 1). Το ρομπότ καθημερινά εκτελεί μία σειρά από εργασίες που πρέπει να γίνουν με προκαθορισμένη σειρά σε κάποιους τομείς (x 1, y 1 ), (x 2, y 2 ),..., (x n, y n ). Το ρομπότ ξεκινώντας από την αρχική του θέση που είναι ο τομέας (0, 0), θα πρέπει να μεταβεί πρώτα στον τομέα (x 1, y 1 ), να εκτέλέσει εκεί την πρώτη εργασία, στη συνέχεια να μεταβεί στον τομέα (x 2, y 2 ) και να εκτέλέσει εκεί τη δεύτερη εργασία και να συνεχίσει με τον ίδιο τρόπο, μέχρι να ολοκληρώσει και την τελευταία εργασία του. Τέλος από τον τομέα (x n, y n ) πρέπει να επιστρέψει στην αρχική του θέση (0, 0). Γράψτε μία συνάρτηση trace σε Haskell, η οποία θα δέχεται ως όρισμα μία λίστα από ζεύγη ακεραίων, η οποία περιέχει τις συντεταγμένες των τομέων στις οποίες θα πρέπει να εκτελεστούν οι εργασίες από το ρομπότ και θα επιτρέφει μία λίστα του ίδιου τύπου, η οποία θα περιέχει όλους τους τομείς που θα επισκεφτεί το ρομπότ ξεκινώντας από τη θέση (0, 0) έτσι ώστε να εκτέλεσει τις απαραίτητες εργασίες σε όλους τους τομείς της λίστας και να επιστρέψει στη θέση (0, 0). Μπορείτε να υποθέσετε ότι η λίστα δεν περιέχει το στοιχείο (0, 0) και ότι διαδοχικές εργασίες εκτελούνται σε διαφορετικούς τομείς. Ο τύπος της συνάρτησης θα πρέπει να είναι [(Int,Int)]->[(Int,Int)] Main> trace [(3,8)] [(0,0),(1,1),(2,2),(3,3),(3,4),(3,5),(3,6),(3,7),(3,8),(2,7),(1,6),(0,5),(0,4),(0,3), (0,2),(0,1),(0,0)] Main> trace [(5,5),(-5,5)] [(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(4,5),(3,5),(2,5),(1,5),(0,5),(-1,5),(-2,5), (-3,5),(-4,5),(-5,5),(-4,4),(-3,3),(-2,2),(-1,1),(0,0)] Main> trace [(5,3),(-2,-6),(-1,1),(3,-7)] [(0,0),(1,1),(2,2),(3,3),(4,3),(5,3),(4,2),(3,1),(2,0),(1,-1),(0,-2),(-1,-3),(-2,-4), (-2,-5),(-2,-6),(-1,-5),(-1,-4),(-1,-3),(-1,-2),(-1,-1),(-1,0),(-1,1),(0,0),(1,-1), (2,-2),(3,-3),(3,-4),(3,-5),(3,-6),(3,-7),(2,-6),(1,-5),(0,-4),(0,-3),(0,-2),(0,-1), 0,0)] Main> trace [(i,2*(-1)^i) i <- [1..12]] [(0,0),(1,-1),(1,-2),(2,-1),(2,0),(2,1),(2,2),(3,1),(3,0),(3,-1),(3,-2),(4,-1),(4,0), (4,1),(4,2),(5,1),(5,0),(5,-1),(5,-2),(6,-1),(6,0),(6,1),(6,2),(7,1),(7,0),(7,-1), (7,-2),(8,-1),(8,0),(8,1),(8,2),(9,1),(9,0),(9,-1),(9,-2),(10,-1),(10,0),(10,1),(10,2),(11,1),(11,0),(11,-1),(11,-2),(12,-1),(12,0),(12,1),(12,2),(11,1),(10,0),(9,0),(8,0), (7,0),(6,0),(5,0),(4,0),(3,0),(2,0),(1,0),(0,0)]
Ασκηση 3. Ονομάζουμε διαμέριση μίας συμβολοσειράς w κάθε ακολουθία αποτελούμενη από δύο ή περισσότερες μη κενές συμβολοσειρές z 1, z 2,..., z k (k 2), η συνένωση των οποίων είναι η w. Γράψτε μία συνάρτηση partition σε Haskell, η οποία θα δέχεται ως όρισμα μία συμβολοσειρά w και θα επιστρέφει μία λίστα με όλες τις διαμερίσεις της w. Κάθε διαμέριση της w αναπαρίσταται ως μία λίστα συμβολοσειρών. Ο τύπος της συνάρτησης θα πρέπει να είναι String->[[String]]. Για έλεγχο χρησιμοποιήστε τις παρακάτω τιμές (οι σειρά των διαμερίσεων στη λίστα μπορεί να είναι διαφορετική από αυτή που φαίνεται στα αποτελέσματα, ωστόσο η σειρά των συβολοσειρών σε κάθε εσωτερική λίστα θα πρέπει να είναι η ίδια): Main> partition "on" [["o","n"]] Main> partition "sun" [["s","un"],["s","u","n"],["su","n"]] Main> partition "band" [["b","and"],["b","a","nd"],["ba","nd"],["b","a","n","d"],["ba","n","d"], ["b","an","d"],["ban","d"]] Main> partition "crazy" [["c","razy"],["c","r","azy"],["cr","azy"],["c","r","a","zy"],["cr","a","zy"], ["c","ra","zy"],["cra","zy"],["c","r","a","z","y"],["cr","a","z","y"], ["c","ra","z","y"],["cra","z","y"],["c","r","az","y"],["cr","az","y"], ["c","raz","y"],["craz","y"]] Main> partition "a" [] Main> length (partition "abcdefghijklmn") 8191
Ασκηση 4. Γράψτε μία συνάρτηση υψηλότερης τάξης hof σε Haskell, η οποία θα δέχεται ως όρισμα μία μη κενή λίστα συναρτήσεων από ακεραίους σε ακεραίους [f 1, f 2,..., f k ] και θα επιστρέφει ως αποτέλεσμα τη συνάρτηση f από ακεραίους σε ακεραίους, η οποία ορίζεται με τον παρακάτω τρόπο: f(n) = k f i(n i + 1) = f 2 i 1 1 (n) + f 2(n 1) + f 3(n 2) +... + f k(n k + 1) 2 4 2 k 1 i=1 Ο τύπος της συνάρτησης θα πρέπει να είναι [Integer->Integer]->(Integer->Integer). Main> map (hof [(+1)]) [1..10] [2,3,4,5,6,7,8,9,10,11] Main> map (hof [(+1),(+2)]) [1..10] [3,4,6,7,9,10,12,13,15,16] Main> map (hof [(2^),(2^),(2^),(2^),(2^)]) [5..12] [42,85,170,341,682,1364,2728,5456] Main> map (hof [(*2),(+100),(^3),negate,(mod 100)]) [24..40] [2768,3151,3567,4020,4509,5038,5609,6221,6876,7575,8322,9117,9960,10855,11805, 12806,13863] Main> map (hof [(mod 100),negate,(+100),(^3),(*2)]) [24..40] [1181,1351,1562,1767,1989,2230,2490,2771,3071,3395,3774,4145,4539,4958,5402,5873,6369] Main> map (hof [( mod i) i<-[50..100] ]) [1000..1030] [23,24,27,28,30,32,35,36,39,40,37,38,40,41,44,45,47,48,52,53,55,31,33,35,37,39,42, 45,46,48,47]
Ασκηση 5. Γράψτε μία συνάρτηση υψηλότερης τάξης combine σε Haskell, η οποία θα δέχεται ως ορίσματα δύο λίστες s = [a 1, a 2,..., a k ] και t = [b 1, b 2,..., b l ] με στοιχεία τύπου u και v αντίστοιχα, δύο συναρτήσεις f και g τύπου u->v->w και μία συνάρτηση h τύπου Int->Bool και θα επιστρέφει ως αποτέλεσμα τη λίστα [c 1, c 2,..., c min(k,l) ] με στοιχεία τύπου w, όπου { f(ai, b c i = i ) αν h(i) g(a i, b i ) αλλιώς Ο τύπος της συνάρτησης θα πρέπει να είναι [u]->[v]->(u->v->w)->(u->v->w)->(int->bool)->[w]. Main> combine [5,4,3,2] [7,8,9,10] (*) (^) odd [35,65536,27,1024] Main> combine [2,2..] [1..20] (*) (^) (\n -> mod n 5 == 1) [2,4,8,16,32,12,128,256,512,1024,22,4096,8192,16384,32768,32,131072,262144,524288, 1048576] Main> combine ["summer","drops","black","white"] ["time","rain","board","snow"] (++) (\x y -> y++x) odd ["summertime","raindrops","blackboard","snowwhite"] Main> combine ["summer","drops","black","white","time","rain","board","snow"] [1..] (\x y -> (x,0)) (\x y -> ("",y)) even [("",1),("drops",0),("",3),("white",0),("",5),("rain",0),("",7),("snow",0)]