Idag: Dataabstraktion Hur använder vi det vi hittills kan om Scheme för att realisera (implementera) sammansatta data? Hur separerar man datastrukturen från resten av ett program så att ändringar i datastrukturen inte påverkar resten av programmet? Bra om du läst följande avsnitt i AS: Inledningen i avsnittet: Building Abstractions with Data Introduction to Data Abstraction DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 1 / 16
Exempel rationella tal Scheme De flesta Scheme-varianterna har någon form av stöd för rationella tal men Scheme väljer ganska fritt hur presentationen skall ske och växlar mellan olika former av presentationsformat Om man vill representera rationella tal exakt så duger alltså inte talrepresentationen i Scheme. Ett rationellt tal kan skrivas som ett bråk: 2 3, 1219 150, 10 15 Vi kan använda en lista med två element för att lagra (representera) ett rationellt tal: > (list 2 3) (2 3) > (list 1219 150) (1219 150) DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 2 / 16
Exempel: rationella tal... Använder vi listor för att lagra täljare och nämnare kan de aritmetiska operationer för rationella tal implementeras på följande sätt, här endast addition och multiplikation: (define +rat (list (+ (* (car x) (cadr y)) (* (car y) (cadr x))) (* (cadr x) (cadr y))))) (define *rat (list (* (car x) (car y)) (* (cadr x) (cadr y))))) DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 3 / 16
Exempel: rationella tal... > (define x (list 2 3)) > (define y (list 3 4)) > (+rat x y) (17 12) > (*rat x y) (6 12) DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 4 / 16
Exempel: rationella tal... I och för sig fungerar den lösningen. Men den kan förbättras. T.ex. kan man minska minnesbehovet genom att använda par istället för listor för att lagra rationella tal. > (define x (cons 2 3)) > (define y (cons 3 4)) > (+rat x y) När det sista uttrycket beräknas, kommer Scheme att klaga på att proceduren cadr har ett par som argument. Man måste byta alla cadr mot cdr. DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 5 / 16
Exempel: rationella tal... (define +rat (cons (+ (* (car x) (cdr y)) (* (car y) (cdr x))) (* (cdr x) (cdr y))))) osv (define *rat... Det krävs många ändringar i stora program när man vill ändra datastrukturen. DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 6 / 16
Dataabstraktion Scheme Vad man kan göra istället är att skriva en uppsättning konstruktorer och selektorer som hanterar datastrukturen. Procedurer som +rat och * rat anropar då inte car och cdr direkt, utan anropar istället konstruktorerna och selektorerna. En konstruktor är en procedur som skapar en (tillämpningsspecifik) typ av dataobjekt. (define make-rat (lambda (numer denom) (list numer denom))) DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 7 / 16
Dataabstraktion... Scheme En selektor är en procedur som returnerar en del av ett (tillämpningsspecifikt) dataobjekt. (define numer (lambda (rat) (car rat))) (define denom (lambda (rat) (cadr rat))) DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 8 / 16
Dataabstraktion... Scheme Med hjälp av konstruktorn och selektorerna kan de aritmetiska operationerna skrivas så: (define +rat (make-rat (+ (* (numer x) (denom y)) (* (numer y) (denom x))) (* (denom x) (denom y))))) (define *rat (make-rat (* (numer x) (numer y)) (* (denom x) (denom y))))) DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 9 / 16
Dataabstraktion... Scheme Vi har lagt till ett isolerande skikt mellan de aritmetiska operationerna och datastrukturen: +rat, *rat,... aritmetiska regler (operationer) make-rat, numer, denom dataabstraktion list, cons, car, cadr, cdr,... datastrukturer och språkprimitiver DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 10 / 16
Dataabstraktion... Scheme Om man nu vill använda par istället för listor, behöver man bara ändra make-rat och denom: (define make-rat (lambda (numer denom) (cons numer denom))) (define denom (lambda (rat) (cdr rat))) DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 11 / 16
Dataabstraktion... Vill man också ta bort gemensamma faktorer i täljaren och nämnaren i ett rationellt tal kan detta göras på två sätt: (define numer (lambda (rat) (let ((g (gcd (car rat) (cdr rat)))) (/ (car rat) g)))) (define denom (lambda (rat) (let ((g (gcd (car rat) (cdr rat)))) (/ (cdr rat) g)))) eller DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 12 / 16
Dataabstraktion... (define make-rat (lambda (numer denom) (let ((g (gcd numer denom))) (cons (/ numer g) (/ denom g))))) där det senare verkar mest naturligt man normaliserar vid konstruktion av ett rationellt tal. DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 13 / 16
Dataabstraktion... Resterande aritmetiska operationer för rationella tal: (define -rat (make-rat (- (* (numer x) (denom y)) (* (numer y) (denom x))) (* (denom x) (denom y))))) (define /rat (make-rat (* (numer x) (denom y)) (* (numer y) (denom x))))) DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 14 / 16
Dataabstraktion... (define =rat (= (* (numer x) (denom y)) (* (denom x) (numer y))))) (define print-rat (lambda (x) (newline) (display (numer x)) (display "/") (display (denom x)))) DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 15 / 16
Nästa gång: Scheme Hur gör man så generella procedurer att de klarar en hel klass av problem, t.ex. alla sorters summeringar, som exempelvis nedanstående? b i = i=a 0 om a > b b a + i annars i=a+1 eller i = a i b, a odd 0 om a > b a + i annars a+2 i b, a odd Läs gärna följande avsnitt i AS: Formulating Abstractions with Higher-Order Procedures men INTE: Procedures as General Methods Procedures as Returned Values DA2001 (Föreläsning 7) Datalogi 1 Hösten 2011 16 / 16