Kodused ülesanded

Üldinfo

Lahenduste esitamise tähtaeg on 6. november 2005. Lahendused salvestada faili nimega FPKodu1.hs ning saata mailiga oma praktikumijuhendajale. Subjektiks peab olema FPKodu1. Iga ülesande eest on võimalik saada kuni 2 punkti (seega kokku kuni 10 punkti). Hilinenud lahenduste korral väheneb punktisumma esimese ülesande juures toodud valemi põhjal.

Vormistamisest

Kõigi ülesannete lahendused vormistada ühe Haskelli failina. Faili nimi peab olema FPKodu1.hs ning seal defineeritava mooduli nimi FPKodu1. Faili algusse lisada kommentaaridena kõigi rühmaliikmete nimed (rühmas võib olla maksimaalselt 2 liiget) ja maili-aadressid. Kõigi ülesannete korral peavad funktsioonide nimed, tüübid ja argumentide arv/järjekord vastama täpselt ülesandes esitatule. Lahendused peavad olema loetavad ja piisavalt kommenteeritud (sh. kui kasutate abiprotseduure, siis see, mida nad teevad, peab olema selgelt välja toodud). Näiteks (FPKodu1.hs):

module FPKodu1 where

---------------------------------------------------------------------
-- FPKodu1.hs
-- Funktsionaalprogrammeerimine - 1. koduülesannete lahendused
--
-- Autorid:
--          Jaan    Tatikas   (tatikas@ut.ee)
--          Salomon Vesipruul (salomon@ut.ee)
---------------------------------------------------------------------

-- 1. ülesanne

hinne :: Ylesanne -> Ylesanne -> Punktid -> Maybe Punktid -> Punktid -> Hinne
hinne koduyl ryhmayl proovieksam eksam boonus = ...

Ülesanded

  1. Kirjutada funktsioon hinne, mis arvutab Teie lõpphinde aines "Funktsionaalprogrammeerimise meetodid". Defineerime järgmised tüübisünonüümid:

    > type Punktid = Double         -- Töö/eksami eest arvestatav punktide arv
    > type Ylesanne = (Punktid, Int) -- Juhendaja poolt antud punktid ja hilinenud päevade arv
    > type Hinne = Char             -- Lõpphinne aines "Funktsionaalprogrammeerimise meetodid"
    

    Funktsiooni signatuur on

    > hinne :: Ylesanne -> Ylesanne -> Punktid -> Maybe Punktid -> Punktid -> Hinne
    > hinne koduyl ryhmayl proovieksam eksam boonus = 
    

    Aines on kaks praktilist tööd: kodutöö ja rühmatöö. Neid peab esitama õigeks ajaks. Iga hilinenud päev toob kaasa trahvi. Töö eest saadud punktide arvu, saate arvutada järgmise funktsiooniga:

    > trahviga :: Ylesanne -> Double
    > trahviga (p,d) = p * tegur
    >   where
    >   tegur = if d == 0 then 1.0 else mini + diff * tempo^(d-1)
    >   mini = 0.4
    >   maxi = 0.8
    >   nadalatrahv = 1/3
    >   diff = maxi - mini
    >   tempo = (1 - nadalatrahv) ** (1/7)
    

    Lõplik hinne arvutatakse järgmiste sisendite põhjal:

    1. koduyl :: Ylesanne
      Koduse töö eest võib saada kuni 10 punkti. Rakendatakse ülaltoodud funktsiooni, mille tulemuseks on täisarv.
    2. ryhmayl :: Ylesanne
      Rühmatöö eest võib saada kuni 30 punkti. Rakendatakse sama funktsiooni.
    3. proovieksam :: Punktid
      Proovieksami eest võib saada kuni 60 punkti. Kui proovieksam on tegemata, siis tuleks arvestada 0 punktiga.
    4. eksam :: Maybe Punktid
      Ka eksami eest on võimalik saada 60 punkti. Kui tudeng on aga rahul oma proovieksami tulemusega, võib ta eksamit mitte sooritada. Sellisel juhul on eksami tulemus Nothing ning arvestada tuleks proovieksami punktidega. Kui tudeng aga ilmub eksamile, siis loevad ainult eksamil saadud punktid.
    5. boonus :: Punktid
      Praktikumijuhendaja poolt antud boonus. Kuni kümme punkti.

    Punktid liidetakse kokku ning ümmardatakse funktsiooniga ceiling. Tulemust hinnatakse skaalas 0-100 (kuigi teoreetiline maksimum on 110). Hinne määratakse ülikooli reeglite järgi:

        91-    --  A
        81-90  --  B
        71-80  --  C
        61-70  --  D
        51-60  --  E
          -50  --  F
    

    Näiteks:

    hinne (10,0) (30,0) 60 Nothing   10    ==>   'A'
    hinne (10,0) (30,0) 60 Nothing    0    ==>   'A'
    hinne (10,0) (30,0)  0 (Just 50)  5    ==>   'A'
    hinne (10,0) (30,0)  0 (Just 50)  0    ==>   'B'
    hinne (10,0) (30,0)  0 (Just 51)  0    ==>   'A'
    hinne (10,0) (30,0) 60 (Just 20)  0    ==>   'E'
    hinne (10,1) (30,0) 60 (Just 20)  0    ==>   'E'
    hinne (10,1) (30,0) 60 (Just 20) 10    ==>   'D'
    hinne (10,0) (30,0) 20 (Just 35)  0    ==>   'C'
    hinne (10,1) (30,1) 20 (Just 35)  0    ==>   'D
    

    Võite eeldada, et sisestatud andmed on korrektsed ning nõutud piirides.

  2. Kirjutada funktsioon bilanss :: String -> Bool, mis suvalise teksti kohta kontrollib, kas ümarsulgude bilanss klapib.

    Näiteks:

    bilanss "(" ==> False
    bilanss "()" ==> True
    bilanss ")(" ==> False
    bilanss "(()())()" ==> True
    bilanss "(2 + 3) * (sin x - sin y)" ==> True
    bilanss (')' : repeat 'Z') ==> False
    bilanss (repeat '(') ==> ⊥ <lõpmatu tsükkel>
    

  3. Reaalarvu x täisosa on suurim täisarv, mis pole suurem kui x. Reaalarvu x murdosa on x ja x täisosa vahe.

    Kirjutada funktsioon detsimaal :: Integer -> Integer -> (Integer , [Integer]), mis, saades ette täisarvud n ja m, annab välja jagatise n/m täisosa ja murdosa kümnendesituse numbrite listina. Seejuures ei tohi list lõppeda nulliga ega nulliga perioodis. Kui teine argument on null, anda olukorda kirjeldav veateade.

    Näiteks:

    detsimaal 0 1       ==> (0 , [])
    detsimaal 1 4       ==> (0 , [2,5])
    detsimaal (-1) 4    ==> (-1 , [7,5])
    detsimaal 1 (-4)    ==> (-1 , [7,5])
    detsimaal (-1) (-4) ==> (0 , [2,5])
    detsimaal 25 16     ==> (1 , [5, 6, 2, 5])
    detsimaal 10 3      ==> (3 , 3 : 3 : 3 : 3 : 3 : 3 : 3 : 3 : 3 : 3 : 3 : ...)
    detsimaal 1 0       ==> ⊥ <töö lõpp veateatega>
    

  4. Funktsioon dropWhileRev eemaldab listi lõpust elemente, kuni argumendina antud predikaat on tõene:

    > dropWhileRev :: (a -> Bool) -> [a] -> [a]
    

    Näiteks:

    dropWhileRev (>3) [1,7,3,5,2,4,7,6]  ==>  [1,7,3,5,2]
    
    dropWhileRev (== 0) []  ==> []
    dropWhileRev (== 0) [0] ==> []
    dropWhileRev (== 0) [1] ==> [1]
    
    dropWhileRev (== 0) (repeat 1)     ==> 1 : 1 : 1 : 1 : 1 : 1 : 1 : 1 : 1 : ...
    dropWhileRev (== 0) (cycle [0, 1]) ==> 0 : 1 : 0 : 1 : 0 : 1 : 0 : 1 : 0 : 1 : ...
    dropWhileRev (== 0) (1 : repeat 0) ==> 1 : ⊥ <lõpmatu tsükkel>
    

    Lõplikel listidel on võimalik see funktsioon defineerida järgnevalt:

    > dropWhileRev p = reverse . dropWhile p . reverse
    

    See on aga ebaefektiivne (vaatab listi läbi kolm korda). Kasutades standardfunktsiooni foldr, kirjutada funktsiooni dropWhileRev efektiivsem definitsioon, mille korral läbitakse listi ainult üks kord; s.o definitsioon peab olema kujul:

    > dropWhileRev p xs = foldr f b xs
    >   where f = 
    >         b = 
    

  5. On antud definitsiooniosa

    > keera :: Picture -> Picture
    > keera = foldl f b
    >   where f = 
    >         b = 
    

    Kirjutada puuduvad lokaalsete muutujate definitsioonid, nii et tulemusena saadav funktsioon keera :: Picture -> Picture keerab pilti 90 kraadi võrra päripäeva. Võib eeldada, et pilt on mittetühi, igas suunas lõplik ja tema kõik read on ühepikkused.

    Näiteks, kui pilt pic1 on defineeritud koodiga

    > pic1 = ["  #  ",
    >         " # # ",
    >         "#   #",
    >         "#####",
    >         "  #  "]
    
    siis
    Main> printPicture (keera pic1)
     ##
     # #
    ##  #
     # #
     ##
    
    (Tüüp Picture ja funktsioon printPicture :: Picture -> IO () on defineeritud loengus.)