Semantyka i Weryfikacja Programów - Laboratorium 3 Cz. I. Rachunek w sklepie Problem. Kasa sklepu zawiera rejestr danych łączących kody artykułów (np. paskowe) z ich nazwami i cenami. Konkretny zakup kasa interpretuje jako listę, gdzie na kolejnych pozycjach znajduje się liczba sztuk i kod danego artykułu. Należy opracować program, który wystawia rachunek z zakupu. Odpowiednio do pozycji na liście zakupu, rachunek ma zawierać liczbę sztuk, nazwę artykułu i ich cenę w formie listy, a na końcu sumaryczny koszt. Analiza Definicje typów danych: type kodart = string - kod artykułu (np. odpowiednik kodu paskowego) type nazwaart = string - nazwa artykułu type cena = int - cena artykułu (jednej lub kilku sztuk) lub sumaryczny koszt zakupów dla uproszczenia przyjęto liczbę całkowitą Dane o artykułach w rejestrze kasy mają postać par (kod_artykulu, (nazwa_artykulu,cena_artykulu)). Rejestr danych (w pamięci kasy sklepowej) jest zatem listą typu: type rejestr = (kodart*(nazwaart*cena)) list rejestr definiowany typ... nil argument lista kodart (nazwaart * cena) typy składowe nazwa Art cena string string int argumenty predefiniowane Niżej podano przykładową zawartość rejestru (bazę danych): val Rejestr = [ ("a1", ("ser bialy", 3)), ("a2", ("szprotki", 2)), ("a3", ("sok", 4)) ]; Klient kupuje kilka artykułów o tym samym kodzie. Wprowadzono nowe określenia typu: type sztukia = int type pozart = sztukia*kodart type zakupy = pozart list - liczba zakupionych sztuk danego artykułu - pozycja artykułu w liście zakupów - lista zakupów - 1 -
Przykład listy zakupionych artykułów: val zakupy = [(4,"a2"),(1,"a1")]; Dla utworzenia rachunku zdefiniowano następujące typy danych: type infoart = sztukia*nazwaart*cena - informacja o artykule w rachunku, gdzie cena jest kosztem łącznym type infolista = infoart list - lista z informacjami o artykułach type rachunek = infolista*cena - lista j.w. i sumaryczny koszt zakupów Przykład rachunku dla zakupów zdefiniowanych powyżej: ([(4, "szprotki", 8), (1, "ser bialy", 3)], 11) Konstruowana jest funkcja o nazwie utworzrachunek typu zakupy*rejestr->rachunek. Powinna ona sygnalizować wyjątek BLEDNY_KOD_ARTYKULU, gdy w rejestrze nie ma kodu szukanego artykułu (powodem może być błędny odczyt z czytnika kodu paskowego). Programowanie 1) Pomocnicza funkcja wyszukująca artykułu w rejestrze na podstawie kodu: szukajart:kodart*rejestr->nazwaart*cena Definicja funkcji: exception BLEDNY_KOD_ARTYKULU fun szukajart(ka, (ka', naz_cen_a)::rej) = (* nazwa, cena *) if ka=ka' then naz_cen_a else szukajart(ka,rej) szukajart _ = raise BLEDNY_KOD_ARTYKULU; 2) Właściwa funkcja tworząca rachunek: fun utworzrachunek([], _) = ([],0) (*lista artykulow, suma*) utworzrachunek((szta,ka)::zak, rej) = let val (naza,cena) = szukajart(ka,rej) val koszta = szta * cena val (Rach, suma) = utworzrachunek(zak,rej) in ((szta,naza,koszta)::rach, koszta + suma) end; Testy szukajart("a38",[]); szukajart("a3", Rejestr); utworzrachunek([],rejestr); utworzrachunek(zakupy,rejestr); Ewentualnie utworzrachunek([(2,"a22")],rejestr); Polecenie. Rozszerz rejestr o kilka artykułów i utwórz funkcję wyszukującą wszystkie artykuły o podanej nazwie. Wsk.: funkcja zwraca listę. - 2 -
Cz.II. Wielomian jako lista Problem. Wielomian a 0 +a 1 x+...+a n x n o współczynnikach a 0, a 1,..., a n, może być przedstawiony jako lista [a 0, a 1,..., a n ] (por. Matlab). Na przykład wielomianowi x 3 +2 odpowiada lista [2,0,0,1] (dla skrócenia współczynnikami są liczby całkowite). Należy zadeklarować następujące funkcje: a) Mnożenie wielomianu przez stałą b) Mnożenie wielomianu Q(x) przez x c) Dodawanie i mnożenie wielomianów. Do mnożenia można wykorzystać następujący wzór rekursywny 0*Q(x) = 0 (a 0 +a 1 x+...+a n x n )*Q(x) = a 0 Q(x)+x((a 1 + a 2 x+...+a n x n-1 )Q(x)) d) Tekstowa reprezentacja wielomianu (string). Opracować dokumentację techniczną oprogramowania (sprawozdanie), która powinna zawierać: specyfikację typów zmiennych i funkcji (funkcja: argument -> wynik) ewentualne wyjątki test oprogramowania plan pełnych testów (zob. Lab. 1) wyniki testów. Typ type wielomian = int list; Mnożenie przez stałą mnozconst: int*wielomian -> wielomian fun mnozconst(_,[]) = [] mnozconst(a,b::bx) = (a*b)::mnozconst(a,bx); mnozconst(7, [2,0,0,1]); Mnożenie przez x mnozx: wielomian -> wielomian fun mnozx ax = 0::ax; val Q = [2,0,0,1]; mnozx Q; Dodawanie i mnożenie wielomianów ++;--: wielomian*wielomian -> wielomian infix 6 ++; infix 7 ** fun [] ++ bx = bx ax ++ [] = ax (a::ax) ++ (b::bx) = (a+b) :: (ax ++ bx); fun [] ** bx = [] (a::ax) ** bx = mnozconst(a,bx) ++ mnozx(as ** bx); [1] ++ [2,3]; [1] ++ []; [1,2] ** [3,4]; - 3 -
Reprezentacja tekstowa Wielomian a 3 +2 zostanie przedstawiony jako 2 + 0 a^1 + 0 a^2 + 1 a^3 nastring: wielomian -> string load "Int"; local fun nastringn([],_) = "" nastringn(a::ax,0) = Int.toString(a) ^ nastringn(ax,1) nastringn(a::ax,n) = " + " ^ Int.toString(a) ^ " a^" ^ Int.toString(n) ^ nastringn(ax,n+1) in fun nastring ax = nastringn(ax,0) end; nastring Q; nastring [1,2,3]; Cz.III. Biuro matrymonialne Problem. Internetowe biuro matrymonialne dysponuje plikiem danych, w którym znajduje się imię i nazwisko, numer telefonu, płeć, rok urodzenia i lista zainteresowań (ew. hobby) każdego klienta. Użytkownik podaje własną płeć, rok urodzenia i listę zainteresowań, w odpowiedzi otrzymując listę potencjalnych kandydatów (imię i nazwisko, numer telefonu), którymi są klienci odmiennej płci, młodsi lub starsi o mniej niż 10 lat, mający przynajmniej jeden wspólny temat na liście zainteresowań. Zaproponować treść funkcji wyszukującej kandydatów. Typy zmiennych type imienazw = string; type telefon = int; type plec = string (* "Z" lub "M" *) type rokur = int; type hobby = string; type zainteresowania = hobby list; type daneosoby = imienazw * telefon * plec * rokur * zainteresowania; type plik = daneosoby list; type danetwoje = plec * rokur * zainteresowania; Funkcja kandydaci: danetwoje*plik -> imienazw*telefon local infix mem; fun x mem [] = false x mem (y::ys) = x=y orelse x mem ys; fun wspelem([],_) = false (* istnieje wspolny element dwu list *) wspelem(x::xs,ys) = x mem ys orelse wspelem(xs,ys); (* zgodnosc: danetwoje * daneosoby -> bool *) fun zgodnosc((p,r,z),(_,_,p',r',z')) = p <> p' andalso abs(r-r') < 10 andalso wspelem(z,z'); - 4 -
in Testy fun kandydaci(_,[]) = [] kandydaci(dt:danetwoje, (do:daneosoby) :: xs) = if zgodnosc(dt,do) then (#1 do, #2 do) :: kandydaci(dt,xs) else kandydaci(dt,xs) end; (* #1 do imienazw, #2 do telefon *) val O1 = ("in1",997,"z",75,["a"]); val O2 = ("in2",998,"m",70,["b"]); val Plik = [O1,O2]; kandydaci(("z",80,["a"]), Plik); kandydaci(("m",70,["b"]), Plik); kandydaci(("m",70,["c","a"]), Plik); - 5 -
Zadania część 1 1. Zadeklaruj funkcję usunnieparzyste, taką że: usunnieparzyste[x 1,x 2,x 3,x 4,...]=[x 2,x 4,...] 2. Podaj deklarację funkcji typu int list -> int option odszukującej najmniejszy element z listy liczb całkowitych. 3. Podaj deklarację funkcji typu int list -> int option odszukującej największy element z listy liczb całkowitych. 4. Zadeklaruj funkcję polacz łączącą elementy listy w pary, tzn. polacz [x 1,x 2,x 3,x 4,...]= [(x 1,x 2 ),(x 3,x 4 ),...]. 5. Zaproponuj deklarację funkcji length wyznaczającej długość listy. 6. Zadeklaruj funkcję krotnosc(x,ys), która podaje ile razy element x występuje na liście ys. 7. Napisz funkcję sprawdzającą, czy na podanej liście występują powtórzenia elementów. Działanie zaprezentuj na przykładzie. 8. Zrealizuj funkcję biblioteczną map typu: ('a -> 'b)->'a list -> 'b list wywołującą podaną funkcję kolejno dla każdego elementu listy: map f [a1,a2,...,an] = [f(a1),f(a2),...,f(an)] 9. Napisz funkcję rev odwracającą kolejność elementów w liście. 10. Podaj definicję funkcji nty typu 'a list*int -> 'a option, która zwraca n-ty element na podanej liście (licząc od zera). 11. Napisz funkcję sprawdzającą, czy wszystkie całkowite elementy listy typu int są nieujemne. 12. Zaproponuj funkcję wstaw typu 'a list * int * 'a: 'a list wstawiającą do listy na podane miejsce (licząc od zera) określony element. - 6 -
Zadania część 2 1. Napisz funkcję sprawdzającą, czy w rejestrze w kasie sklepowej nie występują powtórzenia kodów kreskowych. 2. Sprawdź, czy wszystkie ceny artykułów w bazie danych sklepu są nieujemne. 3. Zmień ceny w rejestrze kasy sklepowej, tak by były wartościami typu real. Oblicz średnią cenę artykułu. Wsk.: może to być kilka funkcji. 4. Dokonaj przeceny artykułu o kodzie Kod o 1/5. Napisz odpowiednią funkcję. 5. Wyszukaj wszystkie artykuły o cenach zawartych w podanym zakresie (min,max). 6. Rozszerz rejestr o daną typu int zawierającą informację o liczbie sztuk w magazynie. Napisz funkcję, która zwraca listę artykułów, których liczba jest mniejsza niż MIN. 7. Wyszukaj za pomocą odpowiedniej funkcji najtańszy artykuł w sklepie. 8. Znajdź n-artykułów najtańszych w sklepie. Utwórz odpowiednią funkcję. Wsk.: najpierw posortuj. 9. Znajdź n-artykułów najdroższych w sklepie. Utwórz odpowiednią funkcję. Wsk.: najpierw posortuj. 10. Wyszukaj za pomocą odpowiedniej funkcji najdroższy artykuł w sklepie. 11. Zadeklaruj funkcje ++, ** z funkcjami mnozconst, mnozx jako lokalnymi ( niewidocznymi z zewnątrz). 12. Zrealizuj funkcję odejmowania wielomianów. - 7 -