Förra gången Förra gången: Rekursiva procedurer I dag I dag: Blockstruktur, omgivningar, problemlösning (define add-1 (define add-2 (lambda (a b) (lambda (a b) (if (= a 0) (if (= a 0) b b (+ 1 (add-1 (add-2 (- a 1) (- a 1) b))))) (+ b 1))))) Tänk: Proceduren add-1 är inte svansrekursiv eftersom den utför en beräkning på resultatet av det rekursiva anropet Tänk: Proceduren add-2 är svansrekursiv eftersom den inte utför någon beräkning på resultatet av det rekursiva anropet Vad är en blockstruktur? Vad är en omgivning? Hur skriver man ett program som löser ett problem? Analys med matematik översättning från matematik till program top-down-metodik se problemet ovanifrån och bryta ner i delar alltefter lösa varje del för sig Bra om du läst igenom i AS: Example: Square Roots by Newton s Method samt Procedures as Black-Box Abstractions Procedures and the Processes They Generate Linear Recursion and Iteration Tree Recursion DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 1 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 2 / 20... Variabler definierade utanför en procedur syns även inuti proceduren: >(define x 1) >(define y 10) >(define sumofthree-1 (+ x y z))) >(sumofthree-1 100) 111 Variablerna x och y är globala i proceduren sumofthree-1, medan z är en lokal variabel. I många situationer vill man gömma vissa definitioner så att de inte används på ett felaktigt sätt. Då kan man ha lokala definitioner: >(define sumofthree-2 (define local-x 1) (define local-y 10) (+ local-x local-y z))) >(sumofthree-2 100) 111 local-x och local-y är lokala variabler i proceduren sumofthree-2. De lokala variablerna är inte definierade utanför proceduren. Om man exekverar uttrycket: >local-x kommer Scheme att klaga över unbound variable. DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 3 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 4 / 20
... Blockstruktur En omgivning (ett scope ) för en variabel är de uttryck för vilka variabeln är definierad. (define sumofthree-1 (define sumofthree-2...))...)) Till ex har den formella parametern z i proceduren sumofthree-1 och den i proceduren sumofthree-2 olika definitioner och skall betraktas som två olika formella parametrar. På samma sätt som man definierar lokala variabler kan man definiera lokala procedurer: >(define sum-of-square (lambda (x y) (define square (* z z))) (+ (square x) (square y)))) >(sum-of-square 3 4) 25 Här har en procedur definierats inuti en annan procedur. DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 5 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 6 / 20 Blockstruktur... Blockstruktur... Man har en blockstruktur där sum-of-square utgör det yttre blocket och square det inre blocket. Proceduren square är inte definierad utanför sum-of-square, så square kan enbart användas inuti sum-of-square. Variabler eller formella parametrar definierade i ett yttre block kan omdefinieras i ett inre block. Det är då den innersta definitionen som gäller i det inre blocket. (define sum-of-square ;;; yttre blocket börjar! (lambda (x y) (define square ;;; inre blocket börjar! (* x x))) ;;; inre blocket slutar! (+ (square x) (square y)))) ;;; yttre blocket slutar! Den formella parametern y är bunden endast i det yttre blocket, medan det finns en bindning av x både i det yttre och det inre blocket. DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 7 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 8 / 20
Exempel: kvadratrot Dra roten ur x: x = y omm y y = x y y = x Men det är inte direkt överförbart till Scheme. Ovanstående är en deklarativ beskrivning, en beskrivning av hur saker förhåller sig till varandra, inte hur beräkningar skall gå till Vi behöver en imperativ beskrivning,en beskrivning av hur (med vilka kommandon) en beräkning utförs, så vi skriver om uttrycket stegvis y = x y y + y = y + x y 2 y = y + x y DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 9 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 10 / 20 2 y = y + x y y = y n+1 = y + x y 2 y n + x y n 2 y n x/y n (y n + x/y n )/2 y n+1 1 2/1 = 2 (2 + 1)/2 1.5 1.5 2/1.5 = 1.3333 (1.3333 + 1.5)/2 1.4167 1.4167 2/1.4167 = 1.4118 (1.4167 + 1.4118)/2 1.4142 1.4142 Det här kan man göra om till ett Scheme-program! Om man gissar ett värde (t.ex. y 0 = 1) och sedan förbättrar detta värde med hjälp av formeln, tills yn+1 2 skiljer sig väldigt lite från x så kan man acceptera y n+1 som x. Fungerar det? Testa med t.ex. x = 2! DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 11 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 12 / 20
(squareroot-iter 1 x))) (if (good-enough? x) (squareroot-iter (improve x) x)))) (/ (+ (/ x )) 2))) I den sista proceduren (improve) binds namnet improve till programmets globala omgivning medan λ-uttrycket definierar en omgivning där namnen och x binds. Namnen och x syns bara inne i proceduren och vi kan använda vilka namn vi vill utan att behöva tänka på namnkollisioner. (lambda (p q) (/ (+ p (/ q p)) 2)) går lika bra men det blir inte lika lätt att förstå vad p och q har för roll. Namnen squareroot-iter, good-enough?, och improve behövs endast då squareroot beräknas så de kan döljas inne i den omgivning som finns inuti squareroot. DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 13 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 14 / 20 Bindningar ;; här börjar lokala definitioner (if (good-enough? x) (squareroot-iter (improve x) x)))) (/ (+ (/ x )) 2)) ;; här slutar lokala definitioner och raden nedan är procedurkroppen till squareroot (squareroot-iter 1 x))) I uttrycket (+ x y) är både x och y fria variabler och det måste finnas både definitioner så man vet vad de betyder och värden så att uttrycket kan beräknas. Dessa måste då finnas i någon yttre omgivning. I uttrycket (lambda(x) (+ x y)) är x bundet. y är fortfarande fritt och måste då vara bundet i någon yttre omgivning till uttrycket. I det senaste squareroot-programmet ändras aldrig x. Så x behöver inte bindas i de lokala procedurerna. Vi kan skapa en blockstruktur där varje lambda-uttryck definierar ett block. får en egen omgivning med egna bindningar. får en gemensam bindning av x. DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 15 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 16 / 20
Definition av lokala variabler (lambda () (if (good-enough? ) (squareroot-iter (improve ))))) (lambda () (lambda () (/ (+ (/ x )) 2))) I stället för define kan man använda let eller let* för att definiera lokala variabler. (define sumofthree-2 (let ((localx 1) (localy 10)) (+ localx localy z)))) Initieringar av lokala variabler i ett let-uttryck görs inte i en specificerad ordning. (squareroot-iter 1))) DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 17 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 18 / 20 Definition av lokala variabler... Nästa gång: Om man vill initiera lokala variabler i en sekvensiell ordning, så ska man använda let* (define sumofthree-3 (let* ((localx 1) (localy 10) (x+y (+ localx localy))) (+ x+y z)))) I vissa fall krävs mer än ett värde för att representera en enhet, t.ex. rationella tal, komplexa tal eller en punkts koordinater i ett plan eller i rymden. Hur ska vi kunna hantera en sådan samling data? Hur ska vi kunna manipulera sådana sammansatta data? Bra om ni läser i AS: Pair i avsnittet: Example: Arithmetic Operations for Rational Numbers Inledningen i avsnittet: Hierarchical Data and the Closure Property Representing Sequences. DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 19 / 20 DA2001 (Föreläsning 5) Datalogi 1 Hösten 2010 20 / 20