Kodutöö 3
Kõrgemat järku listifunktsioonid
Nagu Javas, on ka Idrises disainimustrid, mis aitavad paremat koodi kirjutada ja koodist kiiremini aru saada. Kõige lihtsamad mustrid Idrises on foldr
, map
ja foldl
.
Selle asemel, et kirjutada
sumList : List Int -> Int sumList [] = 0 sumList (x :: xs) = x + sumList xs
kirjutame hoopis
sumList : List Int -> Int sumList = foldr (+) 0
Kõige tähtsamad kõrgemat järku funktsioonid
-- foldr (#) a [x1, x2, ..., xn] = x1 # (x2 # (... # (xn # a))) foldr : (a -> b -> b) -> b -> List a -> b foldr _ a [] = a foldr f a (x :: xs) = f x (foldr f a xs) -- foldl (#) a [x1, x2, ..., xn] = (((a # x1) # x2) # ...) # xn foldl : (b -> a -> b) -> b -> List a -> b foldl _ a [] = a foldl f a (x :: xs) = foldl f (f a x) xs -- map f [x1, x2, ..., xn] = [f x1, f x2, ..., f xn] map : (a -> b) -> List a -> List b map _ [] = [] map f (x :: xs) = f x :: map f xs
Konspekt
Lisalugemine (vabatahtlik):
Type-Driven development with Idris peatükid:
- Chapter 2. Getting started with Idris, alampeatükid:
- 2.2. Functions: the building blocks of Idris programs, alampeatükid:
- 2.2.5. Higher-order function types
- 2.2.6. Anonymous functions
- 2.2. Functions: the building blocks of Idris programs, alampeatükid:
Praktikumi ja kodutöö ülesanded
Tagasiside
Alates sellest praktikumist palume anda tagasisidet kodutöö mahu kohta koos kodutööga. Võib kirjutada tervele kodutööle kulunud aja või iga ülesande kohta eraldi või mingi kombinatsioon nendest. Kirjutage, kui mingi ülesanne võtab ebamõistlikult palju aega.
1. Funktsioon filter'
(standardteegis filter
moodulis Data.List
)
Kirjuta funktsioon filter'
, mis tagastab etteantud listi kõik need elemendid millel esimese argumendina antud funktsioon tagastab True
.
Näiteks: filter' (<3) [3,0,1,3,2,3] == [0,1,2]
.
filter' : (a -> Bool) -> List a -> List a filter' = ?rhs_filter1
2. Funktsioon nullid
Kirjuta funktsioon, mis arvutab nullide arvu etteantud listis.
Näiteks: nullid1 [2,0,1,0,2] = 2
.
Lahenda ülesanne:
- lihtrekursiooniga
nullid1 : List Int -> Int nullid1 xs = ?rhs_nullid1
foldr
-iga
nullid2 : List Int -> Int nullid2 xs = ?rhs_nullid2
- funktsioonidega
sum
jamap
nullid3 : List Int -> Int nullid3 xs = ?rhs_nullid3
- funktsioonidega
length
jafilter'
nullid4 : List Int -> Nat nullid4 xs = ?rhs_nullid4
- listikomprehensiooniga ja funktsioonidega
sum
võilength
nullid5 : List Int -> Int nullid5 xs = ?rhs_nullid5
3. Funktsioon length'
Kirjuta listi pikkuse leidmise funktsioon kasutades foldl
-i.
length' : List a -> Int length' = ?rhs_length
4. Funktsioon productList
Kasutades foldr
, kirjuta funktsioon productList
, mis tagastab listis olevate täisarvude korrutise.
productList : List Int -> Int productList = ?rhs_productList
Näiteks
productList [] ==> 1 productList [3,4,5] ==> 60
Redutseeri avaldis productList [3,2,0]
.
5. Funktsioon append'
Kirjuta listide konkateneerimise operatsioon kasutades foldr
funktsiooni.
(Mitte kasutada sisseehitatud listide konkateneerimise operaatorit (++)!)
append' : List a -> List a -> List a append' xs ys = ?rhs_append'
6. Funktsioon all'
Kasutades foldr
, kirjuta funktsioon all'
, mis võtab argumendiks predikaadi ja listi.
Tagasta True
ainult siis, kui kõik listi elemendid rahuldavad predikaati.
(Vihje: &&
on konjunktsiooni operaator)
isEven : Nat -> Bool isEven Z = True isEven (S Z) = False isEven (S (S n)) = isEven n all' : (a -> Bool) -> List a -> Bool all' = ?rhs_all'
Näiteks:
all' isEven [1, 2, 3] == False all' isEven [2, 4, 6] == True
7. Funktsioon reverse'
Kirjuta listi ümberpööramise funktsioon reverse'
, kasutades foldl
-i.
reverse' : List a -> List a reverse' = foldl rev df where df : List a df = ?rhs_df_rev rev : List a -> a -> List a rev x y = ?rhs_rev
Näiteks:
reverse' [] ==> [] reverse' [1,2,3] ==> [3,2,1]
8. Funktsioon eemaldaNullid
Kirjuta foldr
-ga funktsioon eemaldaNullid
, mis eemaldab listist kõik nullid.
Teised listi elemendid peavad jääma samasse järjekorda.
eemaldaNullid : List Int -> List Int eemaldaNullid = foldr rem df where df : List Int df = ?rhs_df_rem rem : Int -> List Int -> List Int rem x y = ?rhs_rem
Näiteks:
eemaldaNullid [] ==> [] eemaldaNullid [1,0,1,0,2] ==> [1,1,2] eemaldaNullid [1,1,0,0,2] ==> [1,1,2] eemaldaNullid [0,0,0,6,0] ==> [6]
9. Funktsioon allEqual
Kasutades mõnda kõrgemat järku funktsiooni (nt foldr
), kirjuta funktsioon allEqual
, mis kontrollib, kas täisarvude listi kõik elemendid on võrdsed.
allEqual : List Int -> Bool allEqual xs = ?rhs_allEqual
Näiteks:
allEqual [] ==> True allEqual [1,2] ==> False allEqual [1,1,1] ==> True allEqual [1,2,1] ==> False
10. Funktsioon unzip'
Kasutades funktsiooni foldr
, kirjuta funktsioon unzip'
, mis jagab paaride listi kaheks eraldi listiks.
unzip' : List (a, b) -> (List a, List b) unzip' = foldr f z where z : (List a, List b) z = ?rhs_z f : (a, b) -> (List a, List b) -> (List a, List b) f = ?rhs_f
Näiteks:
unzip' [(1,'x'), (4,'1'), (2,'p')] == ([1, 4, 2], ['x', '1', 'p']) unzip' [] == ([], [])
Lisaülesanded
11. Funktsioon removeAll
Kirjuta kaheargumendiline funktsioon, mis võtab argumendiks arvu ja
arvude listi. Funktsioon peab tagastama listi, mis on saadud teisest argumendist kõigi esimese argumendi esinemiste ära jätmisel. Näiteks: removeAll1 2 [2,0,1,3,2] = [0,1,3]
Lahenda ülesanne:
foldr
-iga
removeAll1 : Int -> List Int -> List Int removeAll1 n xs = ?rhs_removeAll1
- funktsiooniga
filter
removeAll2 : Int -> List Int -> List Int removeAll2 n xs = ?rhs_removeAll2
- listikomprehensiooniga
removeAll3 : Int -> List Int -> List Int removeAll3 n xs = ?rhs_removeAll3
12. Funktsioon any'
Kasutades foldr
-i, kirjuta funktsioon any'
, mis võtab argumendiks predikaadi ja listi.
Tagasta True
ainult siis, kui leidub listi element, mis rahuldab predikaati.
(Vihje: ||
on disjunktsiooni operaator)
any' : (a -> Bool) -> List a -> Bool any' p xs = ?rhs_any'
Näiteks:
any' isUpper ['T', 'e', 'r', 'e', '!'] ==> True -- kuna isUpper 'T' == True any' isUpper ['t', 'e', 'r', 'E', '!'] ==> True -- kuna isUpper 'E' == True any' isUpper ['t', 'e', 'r', 'e', '!'] ==> False
Tärnülesanded
Rekursor foldr
Andmetüüpide teooria ütleb, et foldr
on listide rekursor, mis tähendab, et foldr
i kaudu saab defineerida kõik listifunktsioonid (kasutamata selleks rekursiooni).
Defineeri funktsioon foldl
(nimega foldl_
), kasutades foldr
i (ilma rekursioonita).
foldl_ : (b -> a -> b) -> b -> List a -> b foldl_ f a xs = ?rhs_foldl_
Vihje: foldr
algväärtuseks võta identsusfunktsioon id
ning foldl
-i algväärtus a
tuleb anda foldr
i neljandaks argumendiks. Nii saame foldr g id xs a === foldl f a xs
võrrandid vastavalt listi pikkusele:
n=0: id a === a
n=1: g x1 id a === f x1 a
n=2: g x2 (g id x1) a === f x1 (f x2 a)
Nüüd jääb vaid tuletada sobilik g
definitsioon, et võrdused kehtiks:
foldl_ : (b -> a -> b) -> b -> List a -> b foldl_ f a xs = foldr ?g id xs a