Procedurer och villkor Rekursiva procedurer (define lessorequal (lambda (x y) (or (< x y) (= x y)))) (define between (lambda (x y z) (and (lessorequal x y) (lessorequal y z)))) > (between 3 4 5) #t > (between 3 2 5) #f Vad är en rekursiv procedur? Hur beräknas en rekursiv procedur? Hur definieras en rekursiv procedur? Mera rekursiva procedurer Svansrekursion Bra om du åtminstone skummat 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 DA200 (Föreläsning 4) Datalogi Hösten 202 / DA200 (Föreläsning 4) Datalogi Hösten 202 2 / Exempel: n-fakultet Exempel: n-fakultet... Vi vill definiera n-fakultet i Scheme. I matematiken kan n-fakultet definieras på följande sätt: n! = 2... (n ) n Då n är känd, säg lika med 4, kan vi beräkna 4! genom att multiplicera fyra heltal: >(define fyra-fakultet (* 2 3 4)) >fyra-fakultet 24 Då n är lika med 6, kan 6! beräknas på motsvarande sätt: >(define sex-fakultet (* 2 3 4 5 6)) >sex-fakultet 720 Hur definierar vi en procedur för n-fakultet då n inte är konstant? Vi kan inte förkorta ett Scheme-uttryck med... : >(define n-fakultet (* 2... (- n ) n)) DA200 (Föreläsning 4) Datalogi Hösten 202 3 / DA200 (Föreläsning 4) Datalogi Hösten 202 4 /
Exempel: n-fakultet... Exempel: n-fakultet... Vi använder istället en annan matematisk definition av n!: n! = som definierar n! med hjälp av (n )! { om n = 0 (basfall) n (n )! om n > 0 För att beräkna n! multiplicerar vi n med det vi får vid beräkning av (n )!, för att beräkna (n )! multiplicerar vi n med det vi får vid beräkning av (n 2)!,... tills vi kommer till 0!, som enligt basfallet är lika med. En Scheme-procedur som utför ovanstående beräkningssteg definieras nära nog exakt som den ovanstående matematiska definitionen: >(define factorial (lambda (n) (if (= n 0) (* n (factorial (- n )))))) Factorial är en rekursiv procedur eftersom procedurkroppen till factorial innehåller ett anrop av factorial själv. Anropet inuti procedurkroppen kallas ett rekursivt anrop. Att använda factorial: >(factorial 4) 24 DA200 (Föreläsning 4) Datalogi Hösten 202 5 / DA200 (Föreläsning 4) Datalogi Hösten 202 6 / Hur beräknas en rekursiv procedur? Generellt sett, så använder man rekursiva procedurer för att utföra en sekvens av upprepade beräkningar, men hur sker beräkningen? Genom att använda substitutionsmodellen, får vi följande beräkningssteg: Först expansionsfasen:. (factorial 4) 2. (if (= 4 0) (* 4 (factorial (- 4 )))) 3. (* 4 (factorial 3)) 4. (* 4 (if (= 3 0) (* 3 (factorial (- 3 ))))) (* 3 (factorial (- 3 ))))) 5. (* 4 (* 3 (factorial 2))) 6. (* 4 (* 3 (if (= 2 0) (* 2 (factorial (- 2 )))))) 7. (* 4 (* 3 (* 2 (factorial )))) 8. (* 4 (* 3 (* 2 (if (= 0) (* (factorial (- ))))))) 9. (* 4 (* 3 (* 2 (* (factorial 0))))) 0. (* 4 (* 3 (* 2 (* (if (= 0 0) (* (factorial (- )))))))) fullt expanderad:. (* 4 (* 3 (* 2 (* )))) DA200 (Föreläsning 4) Datalogi Hösten 202 7 / DA200 (Föreläsning 4) Datalogi Hösten 202 8 /
. (* 4 (* 3 (* 2 (* )))) så en avvecklingsfas: 2. (* 4 (* 3 (* 2 ))) 3. (* 4 (* 3 2)) 4. (* 4 6) 5. 24 Under expansionsfasen blir uttrycket normalt större och större (eller längre och längre) via substitutionerna. Under avvecklingsfasen blir uttrycket normalt mindre och mindre (eller kortare och kortare). För vissa rekursiva procedurer kan expansionsfasen och avvecklingsfasen falla samman. För att se expansionsfasen och avvecklingsfasen för rekursiva procedurer på skärmen, använd trace: DA200 (Föreläsning 4) Datalogi Hösten 202 9 / DA200 (Föreläsning 4) Datalogi Hösten 202 0 / Mer exempel på rekursion >(trace factorial) >(factorial 4) expansions-fas: [factorial 4] [factorial 3] [factorial 2] [factorial ] [factorial 0] avvecklings-fas: 2 6 24 Gör en procedur som givet två argument, bas och tal, ger det minsta naturliga talet n så att bas n > tal. Detta kan beräknas så:. Testa först om bas 0 > tal. Om villkoret är uppfyllt, så är 0 det n vi söker eftersom 0 är det minsta naturliga talet. 2. Annars, testa om bas > tal. I så fall är n = det sökta talet. 3. Annars, testa om bas 2 > tal. I så fall är det n = 2 vi söker. 4.... DA200 (Föreläsning 4) Datalogi Hösten 202 / DA200 (Föreläsning 4) Datalogi Hösten 202 2 /
Mer exempel på rekursion... Felsökning Denna beräkningssekvens fortsätter tills vi hittat ett n så att bas n > tal. Det funna värdet på n returneras som resultat. I Scheme kan vi definiera en rekursiv procedur som utför ovanstående beräkning. (define power-close-to (lambda (bas tal n) (if (> (expt bas n) tal) n (power-close-to bas tal (+ n ))))) Använd trace för att se vilka argumentvärden som används och vilka värden som returneras i mellanstegen. >(trace power-close-to) >(power-close-to 4 80 0) [power-close-to 4 80 0] [power-close-to 4 80 ] [power-close-to 4 80 2] [power-close-to 4 80 3] [power-close-to 4 80 4] 4 >(untrace power-close-to) >(power-close-to 4 80 0) 4 DA200 (Föreläsning 4) Datalogi Hösten 202 3 / DA200 (Föreläsning 4) Datalogi Hösten 202 4 / Svansrekursion För att utföra exponentberäkningar av typen b n, där n är ett positivt heltal, kan följande rekursiva definition användas: b n = b b (n ) b 0 = Detta kan översättas till schemeproceduren: (define expt- (lambda (b n) (if (= n 0) (* b (expt- b (- n )))))) Gör vi trace på expt- så får vi följande utskrift: DA200 (Föreläsning 4) Datalogi Hösten 202 5 / DA200 (Föreläsning 4) Datalogi Hösten 202 6 /
> (trace expt-) (expt-) > (expt- 2 4) [expt- 2 4] [expt- 2 3] [expt- 2 2] [expt- 2 ] [expt- 2 0] 2 4 8 6 6 Multiplikationerna görs efter de rekursiva anropen. Innan multiplikationerna börjar beräknas, växer uttrycket på grund av de rekursiva anropen. Detta gör att mer minnesuttrymme krävs ju fler rekursiva anrop som utförs. DA200 (Föreläsning 4) Datalogi Hösten 202 7 / DA200 (Föreläsning 4) Datalogi Hösten 202 8 / En alternativ definition av exponentberäkningen: Nu får vi följande utskrift: (define expt-2 (lambda (b n) (expt-2-svans b n ))) (define expt-2-svans (lambda (b n res) (if (= n 0) res (expt-2-svans (- n ) (* b res))))) (trace expt-2-svans) > (expt-2 2 4) [expt-2-svans 2 4 ] [expt-2-svans 2 3 2] [expt-2-svans 2 2 4] [expt-2-svans 2 8] [expt-2-svans 2 0 6] 6 6 Multiplikationerna görs samtidigt som rekursiva anropen, vilket gör uttrycken i mellanstegen inte växer. Inget extra minnesutrymme krävs för de rekursiva anropen. Sådana rekursiva procedurer kallas för svansrekursiva procedurer. DA200 (Föreläsning 4) Datalogi Hösten 202 9 / DA200 (Föreläsning 4) Datalogi Hösten 202 20 /
Factorial definierades rekursivt, men kan naturligtvis få en svansrekursiv definition: (define fac-2 (lambda (n) (fac-2-svans n 2 ))) (define fac-2-svans (lambda (n m res) (if (> m n) res (fac-2-svans n (+ m ) (* res m))))) (trace fac-2-svans) >(fac-2 5) [fac-2-svans 5 2 ] [fac-2-svans 5 3 2] [fac-2-svans 5 4 6] [fac-2-svans 5 5 24] [fac-2-svans 5 6 20] 20 20 Den svansrekursiva proceduren ger upphov till en iterativ process. DA200 (Föreläsning 4) Datalogi Hösten 202 2 / DA200 (Föreläsning 4) Datalogi Hösten 202 22 / Rekursiv procedur rekursiv process (define add- (lambda (a b) (if (= a 0) b (+ (add- (- a ) b))))) [add- 3 4] [add- 2 4] [add- 4] [add- 0 4] 4 5 6 7 Tänk: Proceduren är inte svansrekursiv eftersom den utför en beräkning på resultatet av det rekursiva anropet Rekursiv procedur iterativ process (define add-2 (lambda (a b) (if (= a 0) b (add-2 (- a ) (+ b ))))) [add-2 3 4] [add-2 2 5] [add-2 6] [add-2 0 7] 7 Tänk: Proceduren är svansrekursiv eftersom den inte utför någon beräkning på resultatet av det rekursiva anropet DA200 (Föreläsning 4) Datalogi Hösten 202 23 / DA200 (Föreläsning 4) Datalogi Hösten 202 24 /
Nästa gång: Kan man definiera variabler och procedurer så att de är dolda? Kan man gömma dem? Vi ska titta lite på Blockstrukturer Omgivningar Enkel problemlösning Läs gärna i AS: Example: Square Roots by Newton s Method samt Procedures as Black-Box Abstractions DA200 (Föreläsning 4) Datalogi Hösten 202 25 /