Datalogi, grundkurs 1 Fiktiv Tentamen Lösningsförslag och kommentarer
1. Lösningsförslaget nedan förutsätter ingenting om filens innehåll och är alltså mer generell än nödvändigt: alfa= ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ alfa += alfa.lower() filnamn = input( filnamn? ) f = open(filnamn) count = 0 for line in f: i = 0 j = len(line) while i < j: while i < j and line[i] not in alfa: i += 1 if i < j and line[i] in Pp : count += 1 while i < j and line[i] in alfa: i += 1 print(count, " ord börjar på p i filen ", filnamn, ".", sep= ) Nedanstående förslag till lösning tar hänsyn till att det bara ska finnas bokstäver, blanka och radbyten filnamn = input( filnamn? ) f = open(filnamn) count = 0 for line in f: words = line.split( ) for w in words: if len(w) > 0 and w[0] in Pp : count += 1 print(count, " ord börjar på p i filen ", filnamn, ".", sep= ) f = open( ord.txt ) text = f.read().lower() x = 0 if text[0] == p: x + = 1 for i in range (0, len(text)): if text[i] == and text [i+1] == p x + = 1 print ( Det finns, x, ord som börjar på p ) Missar \np, hade funkat om man först bytt ut \n mot (-2p) Kraschar om filen slutar med ett mellanslag (utanför indexgränsen) (-1p) 1
def count(): f = open( textfil ) s = f.read() # Adds a blank space in front of the string s = " " + s # Replaces \n with blank space s = s.replace( \n, ) # Converts the whole string to lower case s = s.lower() # Now every word will be preceded with s = s.split( p ) summa = s.count() return summa Perfekt! def ordmedp(): textfil = (...) try: f = open(textfil) except IOError: print("filen finns inte eller kan inte läsas!") else: alt = f.read().lower() orden = alt.replace("/n"," ").split() f.close() antal = 0 for i in orden: if i[0] == "p": antal += 1 print(antal, "ord börjar med p") Programmet printer inte en lämplig felmeddelande om man matar in ctrl-c. Bra! Funkar (sånär som på att /n, ska vara \n, inget avdrag för slarvfel som python-interpretatorn skulle tagit hand om). Men... ingen har frågat efter felhanteringen. Inget avdrag naturligtvis men slösa inte tid på sånt vi inte frågar efter! 2. a. Eftersom båda procedurerna tar listan som argument så kan de placeras var som helst i programmet (Era svar är allihop OK) b. (define find-max (lambda (lista) (define inner (lambda (lista max) 2
(cond ((null? lista) max) ((> (car lista) max) (inner (cdr lista) (car lista))) (else (inner (cdr lista) max))))) (inner (cdr lista) (car lista)))) (define (find-max lst) (cond ((empty? (cdr lst)) lst) ((< (car lst) (cadr lst)) (find-max (cdr lst))) ((> (car lst) (cadr lst)) (find-max (cons (car lst) (cddr lst)))) (else (find-max (cdr lst))))) find-max returnerar nu en lista och inte ett element. Åtgärdas genom att ändra till (car lst) i basfallet. (- 2p) (define find-max (lambda (lista) (define inner (lambda (lista max) (cond ((null? lista) max) (( > (car lista) max) (inner (crd lista) (car lista))) (else (inner (cdr lista) max))))) (inner lista (car lista)))) Funkar bra men man kan också anropa med (inner (cdr lista) (car lista)) (i kodens sista rad). c. (define delete-from-list (lambda (lista el) (if (= (car lista) el) (cdr lista) (cons (car lista) (delete-from-list (cdr lista) el))))) (define (delete-from-list lista el) (define (loop lista res) (cond ((null? lista) (reverse res) ((= el (car lista)) (loop (cdr lista) res)) (else (loop (cdr lista) (cons (car lista) res))))) (loop lista ())) Här kommer alla förekomster av elementet el att tas bort (-2p). (define (delete-from-list lst e) (cond ((empty? (cdr lst)) (if (equal? (car lst) e) () 3
lst)) ((equal? (car lst) e) (delete-from-list (cdr lst) e)) (else (cons (car lst) (delete-from-list (cdr lst) e))))) Här kommer alla förekomster av elementet e att tas bort (-2p). 3. a. def cardinal(q): tmp=createqueue() antal = 0 while not empty(q): enqueue(tmp, dequeue(q)) antal += 1 while not empty(tmp): enqueue(q, dequeue(tmp)) return antal def cardinal(q): NyQ = createqueue() antal = 0 while not empty(q): antal += 1 enqueue(nyq, dequeue(q)) return antal, NyQ Kön som hålls av q kommer att tömmas och kommer att vara tom efter det att cardinal körts. Att NyQ returneras kan vara en bra idé men garanterar inte i sig att q återställs. För att q ska återställas måste man anropa på rätt sätt. (-1p om man inte redovisar hur anropet sker) Fallgrop: att man tömmer kön då man räknar antalet element! b. def reverse(q): if not empty(q): e = dequeue(q) reverse(q) enqueue(q, e) return Obs! Man behöver inte returnera kön q, förändringarna som görs i kön i funktionen reverse gäller även utanför den. def reversequeue(q): lista = list() while not empty(q): lista.insert(0, dequeue(q)) for i in lista: enqueue(q, i) Bra lösning som är fullt möjlig i Python. 4
4. a. I ett blackbox-test testar man en algoritm utan annan kunskap än vad den förväntas utföra. Man testar med data som gör resultatet enkelt verifierbart, med typiska indata, med indata på gränsen till vad som ska kunna hanteras och med felaktig indata (om programspråket tillåter det) Helst ska ett blackbox-test utföras av någon som inte känner till implementationen, d.v.s. inte har kunskap om källkoden. (Era svar är allihop OK) b. Ett blackboxtest för deep-sum: 1. Enkelt verifierbara värden: (), (1), ((1)), (1 (1 (1 (1)))) samt samma med element som inte är tal 2. Typiska värden: (1 2 3), (1 (2 (3))), (1 2 (3)) osv 3. Gränsvärden: (10 10, 10 10 ), mm 4. Felaktiga värden: 1, "kalle" Verifikation (jag nöjer mig med två (tre?) uttryck här) (deep-sum (1 (1 (1 ( kalle 1) "hoppsan") #f))) => 1 + (deep-sum ((1 (1 ( kalle 1) "hoppsan") #f))) => 1 + (deep-sum (1 (1 ( kalle 1) "hoppsan") #f)) => 1 + 1 + (deep-sum ((1 ( kalle 1) "hoppsan") #f)) => 1 + 1 + (deep-sum (1 ( kalle 1) "hoppsan")) => 1 + 1 + 1 + (deep-sum (( kalle 1) "hoppsan")) => 1 + 1 + 1 + (deep-sum ( kalle 1)) => 1 + 1 + 1 + (deep-sum 1) => 1 + 1 + 1 + 1 + (deep-sum ("hoppsan")) => 1 + 1 + 1 + 1 + (deep-sum (#f)) => 4 = OK (deep-sum 1) kraschar, ingen koll av att input är en lista så (deep-sum #f) kraschar också Alltså fungerar inte deep-sum enligt specifikationen Ger förslag på en lista man kan skicka in till de olika testerna Lätta värden: (1 2 4 5) Gränsvärden: (Finns väl inga direkta gränsvärden att testa, men kollar så den klarar av en lista i djup 4.) ((((3 4))) 3 4) Typiska värden: (1 2 (3 4 5) (4 5) 4) Felaktiga värden: (ger funktionen ett par) (2.1) Black box-test av Scheme proceduren deep-sum: 5
Lätta värden: (deep-sum (0 0)) --> RÄTT (+ 0 (deep-sum (0))) --> 0 RÄTT (deep-sum (1 2)) --> RÄTT (+ 1 (deep-sum (2))) --> 3 RÄTT Felaktiga värden: (deep-sum ( hej)) --> 0 RÄTT (deep-sum 14) --> Antingen crasher programmet vid (car lista) eller också printer den 0. Båda är inte bra!! Typiska värden: (deep-sum (7 (1 4) 3)) --> RÄTT (+ 7 (+ (+ 1 (+ 4 0)) (+ 3 0))) --> 15 RÄTT Gränsvärden: (deep-sum (1 0.99999999999999999999999)) -> 2, tror jag, men man måste testa. OK Funktionen bör med ett enkelt värde så som (1 (1 1)), vanlig input så som (1 (2 3 (1 4) 4) 4), gränsvärden så som väldigt stora liststrukturer eller listor med stora och små tal. Slutligen bör dålig input undersökas. Vad händer när input är en sträng eller när liststrukturer med både tal strängar eller andra värden blandas. Ignoreras saker som inte inte är tal, kraschar processen eller räknas dessa om till ett värde är olika möjligheter som bör undersökas. OK 5. a. Nej deep-sum är inte svansrekursiv eftersom den i Nej, funktionen är inte svansrekursiv. Den är inte svansrekursiv pga av att det görs en beräkning på det rekursiva anropet. Det görs beräkningar på alla rekursiva anrop, vilket betyder att ingen av dessa anrop är svansrekursiva. Perfekt! b. Vi kom underfund med att deep-sum inte enkelt kan skrivas svansrekursivt (lärarna får skämmas) Det här är inte direkt en uppenbar lösning (define deep-sum (lambda (x) (define inner 6
(lambda (x rest res) (cond ((and (null? x) (null? rest)) res) ((null? x) (inner rest () res)) ((list? (car x)) (inner (cdr x) (append rest (car x)) res)) ((number? (car x)) (inner (cdr x) rest (+ (car x) res))) (else (inner (cdr x) rest res))))) (trace inner) (inner x () 0))) (define deep-sum (lambda (lista) (define inner ;;den inre funktionen tar tre argument (lambda (lista lista2 sum) (cond ((and (null? lista) (null? lista2)) sum) ;;basfall ((null? lista) ;; eftersom vi för över vår ursprungliga (inner lista2 () sum)) ;;lista till lista2 när vi möter en lista ;;i listan måste vi föra tillbaka lista2 ;;till listan för att fortsätta. ((list? (car lista)) ;;ser ifall listan har en lista i sig (inner (car lista) (cdr lista) sum)) ;;svansrekursiva anropet, ;;vi sätter resten av listan till ;;lista2 och den inre listan till lista ((number? (car lista)) ;;om första elementet inte är en lista ;;kollar den om det är en siffra (inner (cdr lista) lista2 (+ sum (car lista))))))) (inner lista () 0))) Bravo! Men, om lista2 innehåller en lista och det dyker upp en till som ska dit så försvinner den ena. Hmm... en illustration behövs nog... (deep-sum (1 2 (3 (4) (5)) (4 5) 4)) -->(inner (1 2 (3 (4) (5)) (4 5) 4) () 0) -->(inner (2 (3 (4) (5)) (4 5) 4) () 1) -->(inner ((3 (4) (5)) (4 5) 4) () 3) -->(inner (3 (4) (5)) ((4 5) 4) 3) -->(inner ((4) (5)) ((4 5) 4) 6) -->(inner (4) ((5)) 6) ;; OBS! ((4 5) 4) försvann 7
-->(inner () ((5)) 10) -->(inner ((5)) () 10) -->(inner (5) () 10) -->(inner () () 15) -->15 (define (svans-deep-sum lista) (define (cond (loop lista res) ((null? lista) res) ((list? (car lista)) (loop (cdr lista) (+ res (loop (car lista) res))) ((number? (car lista)) (loop (cdr lista) (+ res (car lista)))) (else (loop (cdr lista) res)))) (loop lista ())) Blir ju dessvärre inte svansrekursiv! c. def deepsum(lista): summa = 0 if not isinstance(lista, list): return 0 for i in lista: if isinstance(i, list): summa += deepsum(i) elif isinstance(i, int): summa += i elif isinstance(i,float): summa += i return summa Men så krångligt ska det inte vara (minus-poäng till lärarna igen!). def deepsum(x): if len(x)==0: return 0 elif isinstance(x[0],list): return deepsum(x[0]) + deepsum(x[1: ]) elif isinstance(x[0], int): return x[0] + deepsum(x[1: ]) else: return deepsum(x[1: ]) Imponerande tänkt! def deep-sum(lista): summa = 0 for i in lista: 8
if isinstance(i, list): summa += deep-sum(i) else: try: summa += i except ValueError: pass return summa Imponerande tänkt! 9