12. Praktikum
Selles praktikumis harjutame lineaarsete tüüpidega arvutamist
Lisalugemine (vabatahtlik):
Idris 2: Quantitative Type Theory in Action peatükid:
- 2: IDRIS 2 AND QUANTITATIVE TYPES
- 3: RESOURCE USAGE PROTOCOLS
Etteantud kood loengust: lineaarne massiiv
import Data.IOArray import Data.List import Control.App import Control.App.Console import System.Random %hide Builtin.(#) %hide Builtin.DPair.(#) infix 5 # infix 6 ## data L : Type -> Type -> Type where (#) : (1 _ : a) -> b -> L a b -- Konstrueerimine: lineaarset resrurssi saab panna esimeseks -- komponendiks, aga siis on terve paar lineaarne. -- Mustrisobitusel: kui kogu paar peab olema lineaarne, siis -- selle esimene komponent on lineaarne. (Show a, Show b) => Show (L a b) where show (x # y) = show x ++ " # " ++ show y data LL : Type -> Type -> Type where (##) : (1 _ : a) -> (1 _ : b) -> LL a b -- Sama, mis L aga mõlemad komponendid lineaarsed. (Show a, Show b) => Show (LL a b) where show (x ## y) = show x ++ " ## " ++ show y -- Lihtsustus: Double-te massiivid (indekseeritud Nat-dega) public export interface Array arr where write : (1 a : arr) -> Nat -> Double -> arr read : (1 a : arr) -> Nat -> L arr Double size : (1 a : arr) -> L arr Nat frmLst : List Double -> arr toLst : (1 a : arr) -> L () (List Double) delArr : (1 a : arr) -> () -- mälu haldamise jaoks on frmLst ebaturvaline, tuleks teha: -- withArray : ((1 a : arr) -> L arr c) -> c --loengust mapArr : Array arr => (Double -> Double) -> (1 _ : arr) -> arr mapArr f a = let a # sizea = size a in h sizea a where h : Nat -> (1 _ :arr) -> arr h Z a = a h (S n) a = let a # v = read a n a = write a n (f v) in h n a --loengust copyAll : Array arr => (1 src : arr) -> (1 dst : arr) -> LL arr arr copyAll src dst = let src # sizes = size src in let dst # sized = size dst in if sizes == sized then h sizes src dst else src ## dst where h : Nat -> (1 _ :arr) -> (1 _ :arr) -> LL arr arr h Z a b = a ## b h (S n) a b = let a # va = read a n b = write b n va in h n a b -- Testimiseks vaja liides implementeerida. -- Ei pea kõigest aru saama. data LinArray = MkLinArray (IOArray Double) newIOArr : List Double -> IO (IOArray Double) newIOArr xs = let l = cast (length xs) in do a <- newArray l let upd = zip [0..l-1] xs traverse_ (uncurry $ writeArray a) upd pure a toListIOArr : IOArray Double -> IO (List Double) toListIOArr a = let l = Data.IOArray.max a in do xs <- traverse (readArray a) [0..l] pure (takeWhileJust xs) where takeWhileJust : List (Maybe b) -> List b takeWhileJust (Just x :: xs) = x :: takeWhileJust xs takeWhileJust _ = [] int2nat : Int -> Nat int2nat x = if x <= 0 then 0 else S (int2nat (x-1)) Array LinArray where frmLst xs = MkLinArray $ unsafePerformIO (newIOArr xs) toLst (MkLinArray a) = unsafePerformIO (do xs <- toListIOArr a pure (() # xs)) delArr (MkLinArray a) = () size (MkLinArray a) = MkLinArray a # (int2nat (max a)) write (MkLinArray a) i v = unsafePerformIO (do ok <- writeArray a (cast i) v pure (MkLinArray a)) read (MkLinArray a) i = unsafePerformIO (do r <- readArray a (cast i) case r of Just v => pure (MkLinArray a # v) Nothing => pure (MkLinArray a # 0)) newLinArr : List Double -> LinArray newLinArr = frmLst
Ülesanne 1 : implementeeri massiivis elementide vahetamist
swap : Array arr => (1 _ : arr) -> Nat -> Nat -> arr swap a i j = ?swap_rhs
Ülesanne 2: quickSort-i tsükkel
See on peaaegu standard quick-sort -- ainult natuke mugandatud Nat
-idega arvutamiseks, mis ei võimalda negatiivseid indekseid. S.t. võib vaadata algoritmi selgitust siin aga pea meeles, et indeks i algab meil nullist.
pseudokood:
loop a pivot j hi i = while (j<=hi){ if (a[j] < pivot) { swap a[i] a[j] i++ } j++ } return i
Kirjuta tsükliga pseudokood ümber rekursiooniga ja if-dega lineaarseks Idris2 koodiks.
loop: Array arr => (1 a : arr) -> (pivot:Double) -> (j:Nat) -> (hi:Nat) -> (i:Nat) -> L arr Nat loop a pivot j hi i = ?loop_rhs test_loop : (List Double, Nat) test_loop = let 1 a = newLinArr [0,1,4,5,3] a # i = loop a 3 0 3 0 -- viimane element (pivot): 3 () # al = toLst a -- esimene indeks: 0 in -- eelviimane indeks: 3 (al,i) -- esimene indeks: 0 -- pivot 3 jääb lõppu -- listi alguses 3-st väiksemad elemendid -- listi lõpus (v.a. viimane) 3-st suuremad elemendid -- i=2 s.t. esimene 3-st suurem element on 2 -- -- Main> :exec printLn test_loop -- ([0.0, 1.0, 4.0, 5.0, 3.0], 2)
Ülesanne 3: quickSort-i partition
pseudokood:
partition a lo hi = pivot = a[hi] // pivot on viimane element i = loop a pivot lo (hi-1) lo // tsükkel, kus viimast elementi ei vaata swap a[i] a[hi] // nüüd vahetame viimase elemendi return i // tagastame pivot-i indeksi
Kirjuta pseudokood ümber lineaarseks Idris2 koodiks.
-- abiks Nat-i vähenamine decr : Nat -> Nat decr 0 = 0 decr (S n) = n partition : Array arr => (1 a : arr) -> (lo: Nat) -> (hi: Nat) -> L arr Nat partition a lo hi = ?partition_rhs test_part : (List Double, Nat) test_part = let 1 a = newLinArr [0,1,4,5,3] a # i = partition a 0 4 -- esimene indeks: 0 () # al = toLst a -- viimane indeks: 4 in (al,i) -- pivot 3 on indeksil i=2 -- listi alguses 3-st väiksemad elemendid -- listi lõpus 3-st suuremad elemendid -- -- Main> :exec printLn test_part -- ([0.0, 1.0, 3.0, 5.0, 4.0], 2)
Ülesanne 4: quickSort-i peafunktsioon
pseudokood:
qs a lo hi = if (lo<hi) { pivot_index = partition a lo hi qs a lo (pivot_indeks - 1) qs a (pivot_indeks + 1) hi } return a
Kirjuta pseudokood ümber lineaarseks Idris2 koodiks.
qs : Array arr => (1 a : arr) -> (lo: Nat) -> (hi: Nat) -> arr qs a lo hi = ?qs_rhs -- sorteerib kogu massiivi quickSort : Array arr => (1 _ : arr) -> arr quickSort a = let a # len = size a in qs a 0 (decr len) test_qs : List Double test_qs = let 1 a = newLinArr [5,4,7,6,1,2] a = quickSort a () # al = toLst a in al -- Main> :exec printLn test_qs -- [1.0, 2.0, 4.0, 5.0, 6.0, 7.0]
Nüüd vaatame IO-d läbi lineaarsete tüüpide
Sarnaselt massiividele ei taha me alles hoida (arvuti) eelnevaid seisundeid.
public export interface MyHasIO world where writeConsole : (1 _ : world) -> String -> world readConsole : (1 _ : world) -> L world String rnd : (1 _ : world) -> Int -> Int -> L world Int --- implementatsioon (pole tähtis) private data World : Type where MkWorld : World MyHasIO World where writeConsole MkWorld xs = unsafePerformIO (do putStrLn xs pure MkWorld) readConsole MkWorld = unsafePerformIO (do l <- getLine pure (MkWorld # l)) rnd MkWorld l h = unsafePerformIO (do l <- randomRIO (l,h) pure (MkWorld # l)) runIO : Show b => ((1 _ : World) -> L World b) -> IO b runIO prg = let MkWorld # y = prg MkWorld in pure y
Ülesanne 5: io-ga programm
NB! Kellel tuleb viga ehk siis: rnd : (1 _ : world) -> Int32 -> Int32 -> L world Int32 Selline probleem, et saab juhuslikult luua hoopis |
- lugeda konsoolist rida s
- genereerida juhuarv i <- [2..6]
- txt = concat (replicate (cast i) s)
- trükkida välja txt ehk i korda sisestatud tekst s
prog1 : MyHasIO world => (1 w : world) -> L world () prog1 w = ?prog1_rhs -- Main> :exec runIO prog1 -- tere -- tereteretere
Ülesanne 6: massiivi kõigi elementide trükkimine
Vaata testi ja täida lüngad!
printArray: (MyHasIO wrld, Array arr)=> (1 a: arr) -> (1 w: wrld) -> LL wrld arr printArray a w = let a # l = size a w ## a = printElem w a l in w ## a where printElem : (1 w: wrld) -> (1 a: arr) -> Nat -> LL wrld arr printElem w a n = ?printElem_rhs testPrint : MyHasIO wrld => (1 w: wrld) -> L wrld () testPrint w = let 1 a = newLinArr [1,2,3,4] w ## a = printArray a w () = delArr a in w # () -- Main> :exec runIO testPrint -- 1.0 -- 2.0 -- 3.0 -- 4.0
Ülesanne 7: massiivi elementide ülekirjutamine käsurealt
fillArray
loeb käsurealt järjest sõnesid v
ja kirjutab (cast v)
masiivi.
Vaata testi ja täida lüngad!
fillArray: (MyHasIO wrld, Array arr)=> (1 a: arr) -> (1 w: wrld) -> (lo:Nat) -> (hi:Nat) -> LL wrld arr fillArray a w lo hi = ?fillArray_rhs testFill : MyHasIO wrld => (1 w: wrld) -> L wrld () testFill w = let 1 a = newLinArr [1,2,3,4] w ## a = fillArray a w 0 3 w = writeConsole w "------" w ## a = printArray a w () = delArr a in w # () -- Main> :exec runIO testFill -- 5 -- 6 -- 7 -- 8 -- ------ -- 5 -- 6 -- 7 -- 8
Ülesanne 8: käsurealt sorteerija
Implementeeri testSort
nii, et loetakse käsurealt neli arvu ja
trükitakse need sorteeritult konsooli. (kasutada quickSort
-i)
testSort : MyHasIO wrld => (1 w: wrld) -> L wrld () testSort w = ?testSort_rhs -- Main>:exec runIO testSort -- 6 -- 8 -- 3 -- 2 -- ------ -- 2 -- 3 -- 6 -- 8