Par og Lister (først et par sider fra forrige uke) Par er byggestener for lister og trær og sammensatte datatyper.

Like dokumenter
Par og Lister (først et par sider fra forrige uke) Par er byggestener for lister og trær og sammensatte datatyper.

Høyere-ordens prosedyrer

INF2810: Funksjonell Programmering. Dataabstraksjon og Trerekursjon

INF2810: Funksjonell Programmering. Lokale variabler. Og trær.

INF2810: Funksjonell Programmering. Lokale variabler. Og trær.

Gjennomgåelse av eksamensoppgaven i HUMIT2710 fra våren 2004

UNIVERSITETET I OSLO

INF2810: Funksjonell Programmering

INF2810: Funksjonell Programmering

Eksamen i HUMIT 2710, Funksjonell programmering, våren Ingen hjelpemidler er tillatt. <resten av forsiden> Side 1 av 7

INF2810: Funksjonell Programmering

INF2810: Funksjonell Programmering

INF2810: Funksjonell Programmering. Lister og høyereordens prosedyrer

INF2810: Funksjonell Programmering. Lister og høyereordens prosedyrer

Side 1. Oppgave 1. Prosedyrer 1.1. Prosedyrene f og g skal begge returnere prosedyrer. a. Skriv f slik at ((f a) b) returnerer summen av a og b.

INF2810: Funksjonell Programmering. Trær og mengder

Memoisering, utsatt evaluering og strømmer

Memoisering, utsatt evaluering og strømmer

Rekursjon og lister. Stephan Oepen & Erik Velldal. 1. februar, Universitetet i Oslo

INF2810: Funksjonell Programmering. Trær og mengder

UNIVERSITETET I OSLO

INF2810: Funksjonell Programmering. Kommentarer til prøveeksamen

INF2810: Funksjonell Programmering. Trær og mengder

INF2810: Funksjonell Programmering. Strømmer

INF2810: Funksjonell Programmering. Strømmer

(define (naer-nok-kuberot? y x) (< (abs (- (kube y) x)) 0.001)) (define (naermere-kuberot y x) (/ (+ (* y 2) (/ x (kvadrat y))) 3))

INF2810: Funksjonell Programmering. Strømmer og utsatt evaluering

Oppgave 1 Minimum edit distance

UNIVERSITETET I OSLO

INF2810: Funksjonell Programmering. Mer om verditilordning. Tabeller. Og strømmer.

INF2810: Funksjonell Programmering. Strømmer og utsatt evaluering

INF2810: Funksjonell Programmering. Mer om verditilordning. Tabeller. Og strømmer.

INF2810: Funksjonell Programmering

INF2810: Funksjonell Programmering. Mer om strømmer

INF2810: Funksjonell Programmering

INF2810: Funksjonell Programmering. Mer om strømmer

Vi skal se på lambda-uttrykk. Følgende er definerte og vil bli brukt gjennom oppgaven

INF2810: Funksjonell programmering: Mer om Scheme. Rekursjon og iterasjon.

INF2810: Funksjonell Programmering. En metasirkulær evaluator, del 2

INF2810: Funksjonell Programmering. En metasirkulær evaluator, del 2

Innlevering 2a i INF2810, vår 2017

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme, del 2

INF2810: Funksjonell Programmering. Eksamensforberedelser

UNIVERSITETET I OSLO

INF2810: Funksjonell Programmering. Huffman-koding

INF2810: Funksjonell Programmering. Mer om verditilordning og muterbare data.

INF2810: Funksjonell Programmering. Mer om verditilordning og muterbare data.

Eksamen i SLI230, vår 2003.

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme, del 2

INF2810: Funksjonell Programmering. Huffman-koding

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme, del 2

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme

INF2810: Funksjonell Programmering. Mengder og lokal tilstand

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme

INF2810: Funksjonell Programmering. Huffman-koding

INF2810: Funksjonell Programmering. Utsatt evaluering og strømmer

Lisp 2: Lister og funksjoner

Løsnings forslag i java In115, Våren 1998

INF2810: Funksjonell Programmering. En metasirkulær evaluator

INF2810: Funksjonell Programmering. En metasirkulær evaluator

INF2810: Funksjonell Programmering. Huffman-koding

INF2810: Funksjonell Programmering. Lister og høyereordens prosedyrer

INF2810: Funksjonell Programmering. Køer, tabeller, og (litt om) parallelitet

INF2810: Funksjonell Programmering. Køer, tabeller, og (litt om) parallelitet

Appendiks A Kontinuasjoner

IN2040: Funksjonell programmering. Trær, mengder og huffmankoding

INF2810: Funksjonell Programmering. Huffmankoding

UNIVERSITETET I OSLO

Definisjon av binært søketre

INF2810: Funksjonell Programmering. Mer om Scheme. Rekursjon og iterasjon.

Innlevering 2b i INF2810, vår 2017

Binære søketrær. Et notat for INF1010 Stein Michael Storleer 16. mai 2013

Memoisering. I de følgende memoiseringeksemplene brukes tabeller, og vi tar derfor først en repetisjon av dette.

Løsnings forslag i java In115, Våren 1996

E K S A M E N. Algoritmiske metoder I. EKSAMENSDATO: 11. desember HINDA / 00HINDB / 00HINEA ( 2DA / 2DB / 2EA ) TID:

Binære søketrær. En ordnet datastruktur med raske oppslag. Sigmund Hansen

INF2810: Funksjonell Programmering. Huffman-koding

Rekursjon. Binærsøk. Hanois tårn.

Definisjon: Et sortert tre

Eksamen iin115, 14. mai 1998 Side 2 Oppgave 1 15 % Du skal skrive en prosedyre lagalle som i en global character array S(1:n) genererer alle sekvenser

INF2810: Funksjonell Programmering. Utsatt evaluering og strømmer

INF2810: Funksjonell Programmering. Mer om Scheme. Rekursjon og iterasjon.

EKSAMEN med løsningsforslag

INF2220: Forelesning 2

KONTINUASJONSEKSAMEN

INF2810: Funksjonell Programmering. Utsatt evaluering og strømmer

KONTROLLSTRUKTURER. MAT1030 Diskret matematikk. Kontrollstrukturer. Kontrollstrukturer. Eksempel (Ubegrenset while-løkke)

Forelesning 2. Flere pseudokoder. Representasjoner av tall. Dag Normann januar 2008 KONTROLLSTRUKTURER. Kontrollstrukturer. Kontrollstrukturer

Norsk informatikkolympiade runde

INF2810: Funksjonell Programmering. Mer om Scheme. Rekursjon og iterasjon.

Norsk informatikkolympiade runde. Sponset av. Uke 46, 2017

UNIVERSITETET I OSLO

LISP PVV-kurs 25. oktober 2012

Binære trær: Noen algoritmer og anvendelser

Object [] element. array. int [] tall

alternativer til sortering og søking binære trær søketrær Ikke-rekursiv algoritme som løser Hanois tårn med n plater

Det er ikke tillatt med andre hjelpemidler enn de to sidene som er vedlagt oppgavesettet. Følgende funksjoner er definert og brukes i oppgaven:

INF2810: Funksjonell Programmering. Muterbare data

Eksamen i IN 110, 18. mai 1993 Side 2 Del 1 (15%) Vi skal se på prioritetskøer av heltall, der vi hele tiden er interessert i å få ut den minste verdi

MAT1030 Forelesning 2

Transkript:

Par og Lister (først et par sider fra forrige uke) Par er byggestener for lister og trær og sammensatte datatyper. Par kan representeres grafiske slik: Som vi ser kan vi bruke cons til å lage par hvis elementer er par hvilket vil si at par er lukket under cons. Figur 1. Lister er sekvenser av par der car-delen i hvert par er en eller annen verdi og cdr-delen selv er et par som gir resten av listen. En liste er dermed en rekursiv struktur. (cons 1 (cons 2 (cons 3 (cons 4 '())))) (1 2 3 4) Figur 2. Listen over kan vi også konstruere vha primitiven list. (list 1 2 3 4 ) (1 2 3 4) 177

Den tomme listen er representert grafisk ved en diagonal strek i cdr-boksen, som vist i figur 2. I koden angis den tomme listen med en tom parentes prefikset med en enkel apostrof: '(). I læreboken brukes verdien nil som synonym for den tomme listen, men nil er ikke definert i R 5 RS Fra cons til liste (cons 1 2) ==> (1. 2) ; ikke en liste, fordi andre argument ikke er en liste (cons 1 '(2)) ==> (1. (2)) ==> (1 2) ; andre argument er en ikke-tom liste (cons 1 '()) ==> (1. ()) ==> (1) ; andre argument er den tomme listen (cons '(1) '()) ==> ((1). ()) ==> ((1)) ; arg. 1 er en ikke-tom liste og arg.2 er den tomme listen (cons '() '(1)) ==> ((). (1)) ==> (() 1) ; arg. 1 er den tomme listen og arg.2 er en ikke-tom liste (cons '() '()) ==> ((). ()) ==> (()) ; arg. 1 og arg.2 er begge den tomme listen Merk at den tomme listen, på linje med bl.a. de enkelte tallene og #t og #f, er et unikt ikke-konstruert objekt som evaluerer til seg selv. 178

Operasjoner på lister Konstruktoren list tar ingen, ett eller flere argumenter og returnerer listen med de gitte argumentene. Listen konstrueres rekursivt vha. cons. (list 1 2 3 4) (cons 1 (list 2 3 4)) I hvert ledd (par) p i en liste er (cons 1 (cons 2 (list 3 4))) (cons 1 (cons 2 (cons 3 (list 4))) (cons 1 (cons 2 (cons 3 (cons 4 '())))) (car p) elementet, verdien, i leddet og (cdr p) resten av en listen. (define L (list 1 2 3 4)) (define M (list (list 1 2) 3 4)) (car L) ==> 1 (car M) ==> (1 2) (cdr L) ==> (2 3 4) (car (car M)) ==> 1 (car (cdr L)) ==> 2 (car (cdr (car M))) ==> 2 (cdr (cdr L)) ==> (3 4) (cdr M) ==> (3 4)...... 179

Hente ut et element fra en gitt posisjon i en liste. (define (list-ref items n) (if (= n 0) (car items) (list-ref (cdr items) (- n 1)))) list-ref kunne ha vært gort mer robust i forhold til en mulig for stor n, ved en ekstra sjekk for tom liste, men dette ville stort sett ikke ha vært hensiktsmessig, siden en slik feil gjerne er et symptom på dårlig logikk, som vi ønsker å luke ut av koden. Scheme-primtiven list-ref er like lite robust som ovenstående. Vi teller elementene fra 0, slik at f.eks. det tredje elementet har ref = 2. Merk at underveis angir n posisjonen til det søkte elementet i den gjenværende lista. Å telle fra 0 i stedet for fra 1 er både naturlig og bekvemt i programmering, - naturlig fordi avstanden til første element i en liste eller vektor = 0 - bekvemt ved modulus(klokke)-sekvenser, der restverdi 0 betyr begynnelsen på en ny runde. (klokka går fra 0 til 24, ikke fra 1 til 25). (list-ref '(1 2 3 4) 2) 3 (list-ref '(1 2 3 4) 5) cdr krever et par, men fikk den tomme listen items n items n (1 2 3 4) 2 (1 2 3 4) 5 (2 3 4) 1 (2 3 4) 4 (3 4) 0 (3 4) 3 (4) 2 () 1 listen er tom men n er ennå ikke 0. 180

Beregne lengden til en liste. (define (length items) (if (null? items) 0 (+ 1 (length (cdr items)))) Iterativ variant (define (length items) (define (length-iter lst n) (if (null? lst) n (length-iter (cdr lst) (+ n 1)))) (length-iter items 0)) 181

Skjøte en liste til en annen (define (append list1 list2) (if (null? list1) ; Basis: list1 er tom, så vi returnerer list2, der alle elementene i list2 ; opprinnelig list1 nå ligger foran første element i opprinn. list2. (cons (car list1) ; Allment: Legg første gjenværende element i list1 foran (append (cdr list1) list2)))) ; sammenskjøtingen av resten av list1 og økende list2. (append '(1 2 3) '(4 5 6)). (cons 1 (append '(2 3) '(4 5 6))) (cons 1 (cons 2 (append '(3) '(4 5 6)))) (cons 1 (cons 2 (cons 3 (append '() '(4 5 6))))) (cons 1 (cons 2 (cons 3 '(4 5 6))))) (cons 1 (cons 2 '(3 4 5 6))))) (cons 1 '(2 3 4 5 6))))) (1 2 3 4 5 6) 182

Avbildning (mapping) av lister Skalering (define (scale-list items factor) (if (null? items) '() (cons (* factor (car items)) (scale-list (cdr items) factor)))) (scale-list '(1 2 3 4 5) 10) (cons (* 1 10) (scale-list '(2 3 4 5) 10)) (cons (* 1 10) (cons (* 2 10) (scale-list '(3 4 5) 10)))... (cons 10 (cons 20 (cons 30 (cons 40 (cons 50 '()))))) (10 20 30 40 50) Map (avbildning) En koblingsoperasjon mellom elementene i to mengder F.eks. mellom tall og tall, som i scale-list, eller mellom tall og tegn, i en tegntabell. Eller m.a.o: Gitt mengdene A og B: En assossiasjon mellom hvert element i A og et eller annet element i B. Eller m.a.o: En avbildning f : A B er en funksjon f slik at det for hver a A finnes et element f(a) B. 183

Filtrering av en liste (define (remove-evens int-list) (cond ((null? int-list) '()) ((even? (car int-list)) (remove-evens (cdr int-list))) (else (cons (car int-list) (remove-items (cdr int-list)))))) (define (filter predicate sequence) (cond ((null? sequence) '()) ((predicate (car sequence)) (cons (car sequence) (filter predicate (cdr sequence)))) (else (filter predicate (cdr sequence))))) (filter odd? '(1 2 3 4 5 6 7 8 9 10))) ==> (1 3 5 7 9) (filter (lambda (x) (= 0 (remainder x 3))) '(1 2 3 4 5 6 7 8 9 10))) ==> (3 6 9) odd? og even? er biblioteksrutiner. 184

Eksempel på lister med annet enn tall (define (count-vowals letters) (cond ((null? letters) 0) ((member (car letters) '(a e i o u y æ ø å)) (+ 1 (count-vowals (cdr letters)))) (else (count-vowals (cdr letters))))) ; er dennet bokstaven i mengden av vokaler? (count-vowals '(i n s t i t u t t f o r i n f o r m a t i k k)) 8 Primitiven member (Se R 5 RS 6.3.2) er et semipredikat som tar en verdi og en liste og returnerer enten listen f.o.m. første forekomst av den gitte verdien, For semipredikater bruker vi konvensjonelt eller #f, hvis verdien ikke ble funnet i listen. ikke et spørsmålstegn sist i navnet. (member 'u '(a e i o u y æ ø å)) ==> (u y æ ø å) (member 's '(a e i o u y æ ø å)) ==> #f (define (vowal? c) (member c '(a e i o u y æ ø å))) (filter vowal? '(p y t h o n)) (y o) (length (filter vowal? '(i n s t i t u t t f o r i n f o r m a t i k k))) 8 185

Generisk mapping (define (map proc items) (if (null? items) '() (cons (proc (car items)) (map proc (cdr items))))) Mapping fra heltall til heltall (map (lambda (x) (* x x x)) '(1 2 3 4)) (1 8 27 64) Mapping fra heltall til boolean (map even? '(1 2 3 4)) (#f #t #f #t) Mapping fra heltall til symbol (map (lambda (x) (if (odd? x) 'odd 'even)) '(1 2 3 4)) (odd even odd even) Skalering ved mapping (define (scale-list items factor) Hvis vi substituerer map med dens definisjon, får vi (map (lambda (x) (* x factor)) items)) scale-list slik den er definert på forrige side. 186

Hierarkiske strukturer trær Her er et lite tre med 6 noder, hvorav 4 er blader, dvs. ikke-par. (define tre (cons (list 1 2) (list 3 4))) Vi merker oss at treet er en liste med tre elementer, hvorav det første selv er en liste. (length tre) 3 Gitt en prosedyre count-leaves som teller bladene i et gitt tre (her 1, 2, 3, 4) får vi. (count-leaves tre) 4 ; implementasjonen står på neste side Om vi dobler treet ved å lage en liste med treet selv og én to kopi, får vi: (define dobbelttre (list tre tre) (((1 2) 3 4) ((1 2) 3 4))) (length dobbelttre) 2 (count-leaves dobbelttre) 8 187

For å telle bladene i et tre, må vi forholde oss til at noen elementer er trær, dvs. lister, mens andre er blader og skal telles. Vi har ikke innført noe liste-predikat, men vi har et par-predikat, Det finnes også en primitiv list? og i og med at en liste er et par, kan vi bruke dette. men den tar vi senere. (define (count-leaves x) (cond ((null? x) 0) ; null? gir #t for den tomme listen #f for alt annet ((not (pair? x)) 1) ; x er et blad, så legg til 1 (else ; x er et ikke-tomt tre, så (+ (count-leaves (car x)) ; tell og legg sammen antall blader i første del (car-delen) (count-leaves (cdr x)))))) ; og antall blader i resten av listen (cdr-delen) (count-leaves '((1 2) 3 4)) / \ (+ (count-leaves '(1 2)) (count-leaves '(3 4))) (+ (+ 1 (count-leaves '(2)) (+ 1 (count-leaves '(4))) (+ (+ 1 (+ 1 (count-leaves '()))) (+ 1 (+ 1 (count-leaves '())))) (+ (+ 1 (+ 1 0))) (+ 1 (+ 1 0))) (+ (+ 1 1) (+ 1 1)) \ / (+ 2 2) 4 Den parallellprosesseringen som er vist her, er ikke reell. Som kjent vil evalueringen av første ledd i et sammensattuttrykk gå til bunns før evalueringen av andre ledd starter. 188

Merk testfølgen i prosedyren over. Den tomme listen tilfredstiller begge testene (null? x) og (not (pair? x)). Hadde vi snudd testrekkeølgen ville vi ha også ha talt med tomme grener. I koden under er testrekkefølgen snudd, og den gir dermed gir galt resultat. (define (count-leaves-and-nil x) (cond ((not (pair? x)) 1) ; x er et blad eller nil ((null? x) 0) ; fanges opp av forrige clause, og slår aldri til (else (+ (count-leaves-and-nil (car x)) ; x er et ikke tomt tre (count-leaves-and-nil (cdr x)))))) (count-leaves '((1 2) 3 4)) 4 (count-leaves-and-nil '((1 2) 3 4)) 6 189

Avbildning av trær Skalering Vi skalerer et tre på tilsvarende måte som vi skalerer en liste, dvs. ved å skalere hvert enkelt data-element (blad). Ellers følger algoritmen samme mønster som bladtellingsalgoritmen. (define (scale-tree tree factor) (cond ((null? tree) '()) ; returner det tomme treet ((not (pair? tree)) (* tree factor)) ; returner skaleringen av bladverdien (else (cons (scale-tree (car tree) factor) ; returner et nytt par bestående av (scale-tree (cdr tree) factor))))) ; skaleringen av venstre og høyre sub-tre Legg nøye merke til at returverdien er et nytt tre ikke en modifikasjon av det opprinnelige 190

(scale-tree ((1 2) 3 4) 2) ; skaleringsfaktoren = 2 tre inn (cons ; ytterste cons ------------- (scale-tree (1 2) 2) ; arg 1 til ytterste cons (cons ; nest ytterste 1 cons ----- 3 4 (scale-tree 1 2) ; arg 1 til nest ytterste 1 cons (* 1 2) 1 2 2 2 ; evaluert arg 1 til nest ytterste 1 cons (scale-tree (2) 2) ; arg 2 til nest ytterste 1 cons (cons ; innerste cons 1 (scale-tree 2 2) ; arg 1 til innerste 1 cons (* 2 2) 4 4--------------------------; evaluert arg 1 til innerste 1 cons (scale-tree () 2) ; arg 2 til innerste 1 cons 1 ()-------------------------; evaluert arg 2 til innerste 1 cons (4)-------------------------; evaluert innerste cons 1 (4)------------------------- ; evaluert arg 2 til nest ytterste 1 cons (2 4)-------------------------; evaluert nest ytterste 1 cons (2 4)------------------------; evaluert arg 1 til ytterste cons (scale-tree (3 4) 2) ; arg 2 til ytterste cons (cons ; nest ytterste 2 cons (scale-tree 3 2) ; arg 1 til nest ytterste 2 cons (* 3 2) 6 6 ---------------------------; evaluert arg 1 til nest ytterste 2 cons (scale-tree (4) 2) ; arg 2 til nest ytterste 2 cons (cons ; innerste 2 cons (scale-tree 4 2) ; arg 1 til innerste 2 cons (* 4 2) 8 8--------------------------; evaluert arg 1 til innerste 2 cons (scale-tree () 2) ; arg 2 til innerste 2 cons ()-------------------------; evaluert arg 2 til innerste 2 cons (8)--------------------------; evaluert innerste 2 cons tre ut (8)--------------------------; evaluert arg 2 til nest ytterste 2 cons ------------- (6 8)-------------------------; evaluert nest ytterste 2 cons (6 8)--------------------------; evaluert arg 1 til ytterste cons ----- 6 8 ((2 4) 6 8)---------------------; evaluert ytterste cons ((2 4) 6 8)----------------------; evaluert scale-tree 2 4 191

Alternativt kan vi skalere treet ved å avbilde hvert subtre slik (define (scale-tree tree factor) (map (lambda (sub-tree) (if (pair? sub-tree) (scale-tree sub-tree factor) ; scalér subtreet (* sub-tree factor))) ; scalér bladet tree)) Her er envariant av ovenstående der vi har gitt den prosedyren vi mapper på, et navn. (define (scale-tree tree factor) (define (scale-sub-tree sub-tree) (if (pair? sub-tree) (scale-tree sub-tree factor) ; scalér subtreet (* sub-tree factor))) ; scalér bladet (map scale-sub-tree tree)) 192

Primitiver og bibliotekstprosedyrer for par og lister (pair? obj) ==> #t hvis obj er et par (merk at den tomme listen ikke er et par) (list? obj) ==> #t hvis obj er en liste (null? obj) ==> #t hvis obj er den tomme listen (cons obj1 obj2) (list obj1...) (append L1 L2...) ==> paret der første og andre element er hhv.obj1 og obj2 ==> listen med de gitte objektene ==> sammenskjøtingen av de gitt listene (to eller flere) (car pair) ==> første element i det gitte paret (cdr pair) ==> andre element i det gitte paret (caar L) ==> (car (car L)) (cadr L) ==> (car (cdr L))... (cdddar L) ==> (cdr (cdr (cdr (car L)))) (cddddr L) ==> (cdr (cdr (cdr (cdr L)))) (length L) ==> antall elementer i listen L (reverse L) ==> listen med elementene i listen L i omvendt rekkefølge (list-ref L k) ==> k'te element i listen L, regnet fra 0. (list-tail L k) ==> sublisten av listen L etter de k første elementene (member x L) ==> sublisten av listen L f.o.m. første forekomst av x eller #f, hvis x ikke finnes i L 193

Sekvensielle operasjoner SICP 2.2.3 Vi ser på to prosedyrer som tilsynelatende gjør nokså ulike ting. Den ene tar et tre som argument og beregner summen av kvadratene av alle odde tall i treet. (define (sum-odd-squares tree) (cond ((null? tree) 0) ; Basis 0: ikke noe mer i dette subtreet ((not (pair? tree)) ; Basis 1: et tall, (if (odd? tree) (square tree) 0)) ; regn det med hvis det er odde. (else ; Almenntilfellet: et tre (+ (sum-odd-squares (car tree)) ; addér summen for car-subtreet (sum-odd-squares (cdr tree)))))) ; og summen for cdr-subtreet Den andre tar et heltall n og lager en liste over alle partall blant de n første fibonacci-tall. (define (even-fibs n) (define (next k) ; Merk at next gir en rekursiv prosess (if (> k n) ; Basis: Vi har talt oss frem til og med n te fibonaccitall, så '() ; returner den tomme listen (let ((f (fib k))) ; Allment: Behandle k te fibonaccitall. (if (even? f) ; Skal det være med, så (cons f (next (+ k 1))) ; legger vi det inn foran de eventuelle etterfølgende, (next (+ k 1)))))) ; og hvis ikke, tar vi bare med de eventuelle etterfølgende. (next 0)) 194

Vi kan fremstille de to prosessene parallelt slik: sum-odd-squares even-fibs regn opp: blader regn opp: heltall filtrér: odde avbild: fibonér avbild: kvadrér filtrér: partall akkumulér: +, 0 akkumulér: cons, () Bytter vi rekkefølgen mellom filtrering og avbildning i den en eller den andre kolonnen, blir parallellen fullstendig. Men for å generalisere, kan vi ikke ta utgangspunkt i prosedyrer der delprosessene er vevd sammen i én og samme prosess. I stedet skiller vi ut de enkelt delprosessene og plasserer dem i en sekvens av prosesser. Det vi gjør får preg av signalbehandling, der vi sender signaler gjennom filtre, forsterkere, omformere og akkumulatorer. 195

Oppregning (enumerering) i en fortløpende flat liste av verdier (flat = ikke et tre) (define (enumerate-interval a b) ; lag en liste med tallene fra og med a til og med b. (if (> a b) '() (cons a (enumerate-interval (+ a 1) b)))) (define (enum-tree tree) ; lag en liste med alle elementene i tree. (cond ((null? tree) nil) ((not (pair? tree)) (list tree)) (else (append (enum-tree (car tree)) ; skjøt sammen enumereringen av venstre subtre (enum-tree (cdr tree)))))) ; og enumereringen av høyre subtre Merk at vi her konverterer fra tre til liste, og derfor bruker append Vi skjøter sammen enumereringen av treet i car-delen og av treet i cdr-delen i løpende node. mens vi i scale-tree (6 sider tidligere) mapper fra et tre til et annet, og derfor bruker cons. Vi rekonstruerer strukturen til treet, men gir bladene nytt innhold. 196

Akkumulering kombinering av elementene i en liste til én verdi f.eks. en liste av tall til en sum (define (accumulate combine init-val seq) ; combine må være en binær (to-arguments) prosedyre (if (null? seq) init-val (combine (car seq) (accumulate combine init-val (cdr seq))))) (accumulate + 0 (enum-interval 1 6)) 21 (accumulate * 1 (enum-interval 1 6)) 720 (accumulate sqrt 1 (enum-interval 1 6)) RT-feil: sqrt tar kun ett arg. (accumulate + 0 (1 2 3 4)) (+ 1 (accumulate + 0 (2 3 4))) (+ 1 (+ 2 (accumulate + 0 (3 4)))) (+ 1 (+ 2 (+ 3 (accumulate + 0 (4))))) (+ 1 (+ 2 (+ 3 (+ 4 (accumulate + 0 ()))))) (+ 1 (+ 2 (+ 3 (+ 4 0)))) (+ 1 (+ 2 (+ 3 4))) (+ 1 (+ 2 7)) (+ 1 9) 10 197

Redefinering av sum-odd-squares og even-fibs (fra 3. forelesning) som operasjonssekvenser: (define (sum-odd-squares tree) (accumulate + 0 (map square (filter odd? (enum-tree tree))))) (define (even-fibs n) (accumulate cons '() (filter even? (map fib (enum-interval 0 n))))) Legg merke til at i det siste eksemplet, der kombinatoren er cons, er returverdien selv en liste. (Dette gjør eksemplet (som er fra SICP) litt tullete, siden vi alt har en liste, returnert fra filter, men det viser parallellen mellom de to operasjonssekvensene.) 198

Sammenhengen mellom bruk av cons og append og nedtelling og opptelling (define (enum n) (if (= n 0) '() (cons n (enum (- n 1))))) (enum 5) (5 4 3 2 1) ; SYNKENDE (define (enum n) (if (= n 0) '() (append (enum (- n 1)) (list n)))) (enum 5) (1 2 3 4 5) ; STIGENDE (define (enum a b) (if (> a b) '() (cons a (enum (+ a 1) b)))) (enum 1 5) (1 2 3 4 5) ; STIGENDE (define (enum a b) (if (> a b) '() (append (list a) (enum (+ a 1) b)))) (enum 1 5) (1 2 3 4 5) ; STIGENDE (define (enum a b) (if (> a b) '() (append (enum (+ a 1) b) (list a)))) (enum 1 5) (5 4 3 2 1) ; SYNKENDE 199

Nøstet avbildning Problem: Gitt et heltall n, finn alle ordnede par (i, j) for 1 i < j n, slik at summen i + j er et primtall! Sluttresultatet skal være en liste med tripler på formen (i j i+j). Eks: (2 5 7) j i output alle par (i, j) mellom 1 og n slik at i < j Dette kan gjøres i en nestet løkke: 2 1 (((1 2 3)) - for hver j fra 2 til n, 3 1 2 ((2 3 5))) - for hver i fra 1 til j 1, 4 1 2 3 ((1 4 5) (3 4 7)) - hvis i + j er et primtall 5 1 2 3 4 ((2 5 7)) - lag en trippel. 6 1 2 3 4 5 ((1 6 7) (5 6 11))) Vi kunne også løpt gjennom i'ene i i j output den yttre og j'ene i den indre løkka: 1 2 3 4 5 6 (((1 2 3) (1 4 5) (1 6 7)) - for hver i fra 1 til n - 1, 2 3 4 5 6 ((2 3 5) (2 5 7))) - for hver j fra i + 1 til n, 3 4 5 6 ((3 4 7)) - hvis i + j er et primtall 4 5 6 () - lag en trippel. 5 6 ((5 6 11))) 200

La oss i første omgang se hvordan dette kan løses ved å gå innenfra og utover (bottom up): (a) Innerst lager vi en trippel fra en to-elements liste: (define (make-pair-sum pair) ; NB! pair er her rent formelt en liste med to elementer (list (car pair) ; parets første tall (cadr pair) ; parets andre tall (+ (car pair) (cadr pair)))) ; summen av parets to tall (b) Så lager vi alle triplene for primtallsparene for fixert j og for i = 1 j - 1: (define (make-prime-pair-segment i j) ; tell opp i fra 1 til j og cons hvert nytt par (cond ((= i j) '()) ((prime? (+ i j)) ; summen er et primtall, så (cons (make-pair-sum (list i j)) ; cons denne triplen (make-prime-pair-segment (+ i 1) j))) ; på de etterfølgende (else (make-prime-pair-segment (+ i 1) j)))) (c) Ytterst lager vi alle triplene for alle j fra 2 til n: (define (make-prime-pair-list j) ; tell ned j fra n til 2 og append hvert nytt segment (if (< j 2) '() (append (make-prime-pair-list (- j 1)) ; Lag første del av listen, og (make-prime-pair-segment 1 j)))) ; skjøt så dette segmentet til listen 201

Ovenstående gir et greit, men spesifikt, program som løser et spesifikt problemet. At løsningen er spesifikk er et resultata av at de ulike operasjonen er vevd sammen, bl.a. ved at vi enumererer, tester og lager trippellisten i en og samme operasjon i make-prime-pair-segment. Det vi er interessert i her, er å se om problemet kan løses i en sekvens av uavhengig standardoperasjoner på lister, her: enumerering, filtrering, mapping og akkumulering. (a) Trippel-konstruktoren blir den samme som i bottom-up-løsningen: (define (make-pair-sum pair) (list (car pair) (cadr pair) (+ (car pair) (cadr pair)))) (b) Dernest trenger vi et predikat for primtallspar: (define (prime-sum? pair) (prime? (+ (car pair) (cadr pair)))) I SICP, både i den løpende teksten og i øvelsene, angis ulike algoritmer for primtallstesting, men ingen er enkle nok til å vises her. 202

Generering av de ordnede parene (c) Lag listen med j'ene ved å enumerere fra 2 til n: (enumerate-interval 2 n) (2 3 n) (d) Lag listen med i'ene ved å enumerere fra 1 til j 1: (enumerate-interval 1 ( - j 1)) (1 2 j-1) (e) Lag parene for én j ved å mappe fra (d) til liste med par (i j) i en kontekst der j er kjent: (map (lambda (i) (list i j)) (enumerate-interval 1 ( - j 1))) ((5 1) (5 2) ) (f) Lag parene for alle j'ene ved å mappe fra (c) til (e): (map (lambda (j) (map (lambda (i) (list i j)) ; (e) (enumerate-interval 1 ( - j 1)))) ; (d) (enumerate-interval 2 n)) ; (c) (((1 2)) ((1 3) (2 3)) ((1 4) (2 4) (3 4))...) 203

Vi fikk en liste med lister av par (((1 2)) ((1 3) (2 3)) ((1 4) (2 4) (3 4))...) men ønsker oss en flat liste av par ((1 2) (1 3) (2 3) (1 4) (2 4) (3 4)...) (g) Akkumuler med append, for å skjøte sammen og løfte opp listene på nivå 2 til topnivålisten: (s. 197) (accumulate append '() (map (lambda (j) ; (f) (map (lambda (i) (list i j)) ; (e) (enumerate-interval 1 ( - j 1)))) ; (d) (enumerate-interval 2 n))) ; (c) Denne listeutflatingen er såpass vanlig at vi lager en egen rutine flatmap for denne: (define (flatmap proc seq) (accumulate append '() (map proc seq))) 204

Cloue'et med flatmap er at append tar to lister og (slik + tar to tall og at append faktisk tar én eller flere lister, er en annen sak returnerer én returnerer ett,) slik at vi, når vi suksessivt legger neste liste til den akkumulerte lista, ender opp med én liste. (accumulate append '() '((a b c) (d e f) (g h i))) (append (a b c) (accumulate append '() '((d e f) (g h i)))) (append (a b c) (append (d e f) (accumulate append '() '((g h i))))) (append (a b c) (append (d e f) (append (g h i) (accumulate append '() '())))) (append (a b c) (append (d e f) (g h i))) (append (a b c) (d e f g h i)) (a b c d e f g h i) 205

(h) Med alt dette på plass får vi følgende løsning: (define (prime-sum-pairs n) (map make-pair-sum ; (a) (filter prime-sum? ; (b) (flatmap (lambda (j) ; (f-g) (map (lambda (i) (list i j)) ; (e) (enumerate-interval 1 ( - j 1)))) ; (d) (enumerate-interval 2 n))))) ; (c) 206