Funkcijsko programiranje: rešeni izpiti (januar 2021) 2019/20 1. rok 1. naloga a) {a: 'a, c: 'b} -> 'c -> 'd {a: 'a option, c: 'b list} -> 'c -> 'd {a: bool option, c: bool list} -> bool -> -> bool b) fun find el sez = #2 (List.foldl (fn (a, z) => if a = el then ((#1 z) + 1, (#2 z) @ [(#1 z)]) else ((#1 z) + 1, (#2 z))) (1, []) sez); 2. naloga fun summarize sez = let fun pomozna sez {a=a, b=b} = case sez of SOME (A {a=a2})::t => pomozna t {a = a + a2, b = b} | SOME (B (ref b2))::t => pomozna t {a = a, b = b + b2} | NONE::t => pomozna t {a=a, b=b} | [] => {a=a, b=b} in pomozna sez {a=0, b=0} end; - A JE TUKI PROU NARJEN UN PRIMER PRI NAVODILIH? OD KJE 5 in 28? A ne bi moglo bit 6 in 10, če js to prou razumem? Ja, so narobe navodila. Hvala :) 3. naloga e1: 1,1, e2: 0,2, e3:1,2, e4: 0,0 (*Lokalno okolje, najslabša rešitev*) (if e1 (letrec ([d2 (e2)] [d3 (e3)]) (+ d2 d2 d3)) (letrec ([d3 (e3)]) (+ d3 d3))) (*Najtežja rešitev z zakasnitvami in sprožitveni*) (letrec ([ev3 e3] [d2 (delay (lambda () e2))]) (if e1 (+ (force d2) (force d2) ev3) (+ ev3 ev3))) (if e1 (+ (force d2) (forc (letrec ([ev3 e3] [ev2 e2]) (if e1 (+ ev2 ev2 ev3) (+ ev3 ev3))) (if e1 (+ (force d2) (force d2) ev3) (+ ev3 ev3)))e d2) ev3) (+ ev3 ev3))) (letrec ([ev3 [d2 (delay (lambda () e2))] 4. naloga def arg_checker(f): def wrapper( *args, **kwargs): if len(args) != 2 and len(kwargs) != 2: print('wrong number of parameters') else if 'password' in kwargs.keys(): if kwargs['password'] == 'koala': f() else: print('password incorrect') else: print('password not given') 2019/20 2. rok 1.naloga datatype sadje = Jabolko | Hruska; fun teza e = case e of Jabolko => 100 | Hruska => 150; fun kalorije e = case e of Jabolko => 80 | Hruska => 120; Lažje je v objektno-usmerjenim, ker vse spremembe naredimo le na enem mestu (naredimo nov razred in implementiramo funkcije), medtem ko pri funkcijskem prog. moramo pri vsaki funkciji dodati nov case. 2. naloga fun repna_obdelava f1 f2 f3 n vhod = let fun pomozna (vhod, acc) = if f1 (vhod) then acc else pomozna (f2(vhod), f3(vhod, acc)) in pomozna (vhod, n) end val potenca_repna = repna_obdelava (fn (x, y) => y=0) (fn (x, y) => (x, y-1)) (fn ((x, y), acc) => acc*x) 1; val vsota_repna = repna_obdelava (fn sez => null sez) (fn sez => tl sez) (fn (sez, acc) => acc+(hd sez)) 0; (* f1 *) (* f2 *) (* f3 *) (* zac. vr. *) (* f1 *) (* f2 *) (* f3 *) (* zac. vr. *) 3. naloga (define preskocni_tok (lambda (#:preskok [preskok 1] . sez) (letrec ([f (lambda (x) (cons (list-ref sez x) (lambda () (f (modulo (+ x preskok) (length sez))))))]) (f 0)))) 4. naloga 1. 3+13+13 = 29 2. 3+13+4 = 20 3. 3+13+13 = 29 4. 3+13+4 = 20 a = 25 b = 23 5. 3+23+23=49 6. 3+23+4=30 7. 3+23+23=49 8. 3+23+4=30 2018/19 1. rok 1.naloga Na kratko (največ dve povedi!) odgovori na naslednja vprašanja: a.) (1t) Kaj je pomanjkljivosti sistema tipov (tipizator), ki je hkrati poln in trden? Če je hkrati in poln, tipizator ne more ugotoviti, če je sistem ustavljiv. Izberemo namreč lahko le 2 od teh 3 lastnosti. b.) (1t) Kombinacija dveh funkcionalnosti programskega jezika vodi do težav pri statičnem sistemu tipov, ki se jih prevajalnik lahko ogne z omejitvijo vrednosti. Kateri dve funkcionalnosti sta to? Polimorfizem + mutacije?? c.) (1t) Kako lahko zmanjšamo odvečno število evalvacij, če namesto funkcije generiramo kodo z makrom? Lahko uporabimo lokalno okolje in v njem evalviramo izraz, lahko evalvacijo zakasnimo (tako lahko tudi na primer preprečimo neskončno rekurzijo) ali pa uporabimo metodo “zakasnitev sprožitev”, kar je vedno najbolj učinkovito (ne pa najbolj enostavno). d.) (2t) Kakšen je podatkovni tip funkcije f? 'a -> 'b -> 'c -> 'd 'a -> 'b -> 'c -> 'd -> 'e -> 'f 'a -> 'b -> 'c -> int option -> int list list -> int option 2.naloga a) datatype ('a, 'b) node = Node of (('b, 'a) node * ('a) * ('b, 'a) node) | fin; b) ; to mi ne dela ravno, ne vem zakaj fun height drevo = case drevo of fin => 1 | Node (l, _, d) => 1 + Int.max (height l, height d); Po mojem je “fin => 0”, ampak to seveda ni razlog zakaj ne dela... 3.naloga (struct par (g r) #transparent) (struct nic () #transparent) ??? 4.naloga fun poz flist arg limit = List.foldl (fn (f, z) => if f (arg) >= limit then z + 1 else z) 0 flist; Klic takšne funkcije: poz [fn x => x - 100, fn x => x + 100] 0 ~101; 2018/19 2. rok 1.naloga a.) (1t) Imejmo sistem tipov (tipizator), ki je poln in trden? Zakaj tak tipizator v praksi ni uporaben? Ker v tem primeru ne more biti ustavljiv, tj. tipizator se lahko ne bo nikoli ustavil. b.) (1t) Minimalno katere lastnosti mora programski jezik oz. njegov sistem tipov (tipizator), da lahko brez uvedbe posebnih novih podatkovnih tipov implementiramo sezname, ki hranijo elemente različnih tipov (kot npr. v Pythonu ali Racketu)? Tipizator mora uporabljati pretežno dinamično preverjanje (konkretno, da ne rabimo vnaprej določiti podatkovnih tipov). f1: int option option -> int f2: int -> int Če bi podali v f1 recimo string option option, bi še vedno šlo v naslednji korak - bi klicalo funkcijo f2, kjer bi se zataknilo, ker stringa ne moremo množiti z int. Prevajalnik nam že po prevajanju izpiše, kakšnega tipa morajo biti argumenti. Če podamo napačne, nam vrne napako. 2.naloga F 4 5 = 5 + 5 + 5 + 5 + 0 = 20 (4 * 5) G 2 10 = 2 ^ 10 = 1024 b) (1t) Opišite, kaj zares delata funkciji F in G. F=a*b G=a^b c) (1t) Ali je katera od omenjenih funkcij vzajemno rekurzivna? Če da, opišite zakaj! Ne, če bi hotela F uporabljati funkcijo G bi morala biti napisana kot vzajemna rekurzija (z and vmes). Tukaj pa je G (ki uporablja F) definirana za F, zato po tem ni potrebe, saj jo že vidi (prevajalnik gre po vrsti). d) (1t) Ali je katera od omenjenih funkcij repno rekurzivna? Če da, jo navedite, če ne, pa opišite, zakaj ne. F ni repno rekurzivna, ker za vsakim rekurzivnim klicem še izvaja operacijo seštevanja. G pa je repno rekurzivna. (Nisem siguren) ???? Če je funkcija repno rekurzivna bi moralo to veljati na vse veje. G kliče funkcijo F ki ni repno rekurzivna in zato tudi G ni repno rekurzivna.???? e) (2t) Če lahko, funkciji F in G pretvorite v repno rekurzivno obliko. Če ne, argumentirajte, zakaj ne. fun F a b = let fun F 0 b acc = acc | F a b acc = F (a-1) b (acc + b) in F a b 0 end fun F2 a b = let fun pomozna(a, b, acc) = if a = 0 then acc else pomozna(a-1, b, acc+b) in pomozna(a, b, 0) end; fun G2 a b = let fun pomozna(a, b, acc) = if b=0 then acc else let val r = G2 a (b-1) in F a r end; in pomozna(a, b, 1) end; 3.naloga A) Nekaj v zvezi s tem, da so tokovi neskončni in zato moremo uporabiti različico folda, ki ima ustavitveni pogoj Dodaten argument v akumulatorju bi bilo število, kolikokrat naj se fold izvede nad tokom (define (foldX/stream func acc stop stream) (if (= stop 0) acc (foldX/stream func (func (car stream) acc) (- stop 1) ((cdr stream))))) Primer uporabe: (define naravna (letrec ([get (lambda (x) (cons x (lambda () (get (+ x 1)))))]) (get 1))) (define (foldX/stream func acc stop stream) (if (= stop 0) acc (foldX/stream func (func (car stream) acc) (- stop 1) ((cdr stream))))) (foldX/stream + 0 5 naravna) Vrne 15. 4.naloga def flatten(fun): # ne dela def flat_rek(): print(fun()) # tako dostopamo, nisem implementiral return flat_rek @flatten def test(): return [ 1, ['b','c'], [[1],[2]] ] 2018/19 3. rok 1.naloga a.) Kaj so to lažno pozitivni primeri? Primeri, ki so negativni, ampak jih tipizator klasificira kot pozitivne. Torej programe brez napake, zazna kot programe z napako. b.) Ali so lažno pozitivni primeri zaželeni ali nezaželeni? Če so zaželeni, zakaj so koristni / če so nezaželeni, zakaj se jim želimo izogniti? So nezaželeni, ker tako nam lahko tipizator zavrne tudi programe, ki bi se pravilno izvedli. c.) Podaj kratek primer lažno pozitivnega primera v poljubnem programskem jeziku in navedi nek drugi programski jezik, v katerem je tvoj podani primer pravilno pozitivni primer. sml: if true then 10 else 10 div “nekaj” (prepoznal bi ga kot pozitivnega) V python-u bi tole delalo ( oz. v kakem drugem jeziku, ki je tudi pretežno dinamičen (preverjanje)). 2.naloga fun f1 (s1, s2) = let fun f1 sez acc = case sez of [] => acc | {comment=_, data= Trip (_, n, _)}::t => f1 t (acc + n) in (f1 s1 0) + (f1 s2 0) end; 3.naloga 4.naloga 14 10 9 19 15 9 b) Kakšen doseg vrednosti privzeto uporablja Python (od dveh, ki smo ju omenili na pred)? dinamični c) Katera vrsta dosega ima prednost pred drugo-slašbo? Naštej tri prednosti. Vrsta dosega: leksikalni ima prednost Prednosti: slajdi 2017/18 1. rok 1.naloga a) SML ima Lažno Pozitivne primere - primer programa je, ko definiramo case za sezname in ne podpremo možnosti, da dobimo na vhod prazen seznam. Program se ne prevede ker moramo definirati vzorec za vsako možnost, mi pa vemo, da ni možno da bo na vhod prišel prazen seznam. Program je negativen a ga sml označi kot pozitivnega (lažno pozitiven). … a ni “match non exhaustive” samo warning? primer iz slajdov je: if True then 10 else 10 div “niz” b) RACKET ima Lažno Negativne primere - primer programa je, ko ne definiramo ustrezne ustavitve na koncu seznama in poskuša preveriti element ki ne obstaja. ??? Racket je tudi mocno tipiziran kar pomeni da ima lazno pozitivne primere??? c) JAIS je i) dinamično tipiziran (tipi izrazov se preverjajo med izvajanjem ii) Krepko/eksplicitno tipiziran (tekom programa se stalno preverja tip iii) Ima leksikalni doseg 2.naloga (define (splosna f1 f2 zacetek konec) (if (> zacetek konec) null (cons (f1 zacetek) (splosna (f2 zacetek) konec)))) (define (stetje zacetek konec) (splosna (lambda (a) a) (lambda (a) (+ a 1) zacetek konec))) (define (sinus zacetek konec) (splosna (lambda (a) (sin a)) (lambda (a) (+ a (/ pi 8)) zacetek konec))) 3.naloga 4.naloga 2017/18 2. rok 1.naloga 2.naloga 3.naloga 4.naloga 2017/18 3. rok 1.naloga 2.naloga 3.naloga 4.naloga Template 1.naloga 2.naloga 3.naloga 4.naloga