10 Meer over functies

Vergelijkbare documenten
Recursie: definitie. De som van de kwadraten van de getallen tussen m en n kan als volgt gedefinieerd worden:

Recursion. Introductie 37. Leerkern 37. Terugkoppeling 40. Uitwerking van de opgaven 40

recursie Hoofdstuk 5 Studeeraanwijzingen De studielast van deze leereenheid bedraagt circa 6 uur. Terminologie

Programmeermethoden. Recursie. week 11: november kosterswa/pm/

Programmeermethoden. Recursie. Walter Kosters. week 11: november kosterswa/pm/

Een eenvoudig algoritme om permutaties te genereren

Als een PSD selecties bevat, deelt de lijn van het programma zich op met de verschillende antwoorden op het vraagstuk.

1 Inleiding in Functioneel Programmeren

Zevende college algoritmiek. 1 april Verdeel en Heers

Tentamen Programmeren in C (EE1400)

Programmeermethoden NA. Week 5: Functies (vervolg)

Achtste college algoritmiek. 12 april Verdeel en Heers. Dynamisch Programmeren

Programmeermethoden. Functies vervolg. Walter Kosters. week 5: 1 5 oktober kosterswa/pm/

Vierde college algoritmiek. 23/24 februari Complexiteit en Brute Force

Achtste college algoritmiek. 8 april Dynamisch Programmeren


Practicum Programmeerprincipes

Inleiding Programmeren 2

Programmeermethoden NA. Week 5: Functies (vervolg)

PROS1E1 Gestructureerd programmeren in C Dd/Kf/Bd

1 Recurrente betrekkingen

HOOFDSTUK 3. Imperatief programmeren. 3.1 Stapsgewijs programmeren. 3.2 If Then Else. Module 4 Programmeren

7 Omzetten van Recursieve naar Iteratieve Algoritmen

9 Meer over datatypen

HOOFDSTUK 3. Imperatief programmeren. 3.1 Stapsgewijs programmeren. 3.2 If Then Else. Informatie. Voorbeeld. Voorbeeld: toegangsprijs

OEFENINGEN PYTHON REEKS 6

Modelleren en Programmeren

Lab Webdesign: Javascript 25 februari 2008

Inleiding Programmeren 2

Modelleren en Programmeren

VOORBLAD SCHRIFTELIJKE TOETSEN

compileren & interpreteren - compileren: vertalen (omzetten) - interpreteren: vertolken

Tentamen Programmeren in C (EE1400)

Modelleren en Programmeren

Computervaardigheden. Universiteit Antwerpen. Computervaardigheden en Programmatie. Grafieken en Rapporten 1. Inhoud. Wat is scripting?

Programmeren in Java les 3

17 Operaties op bits Bitoperatoren en bitexpressies

Volledige inductie. Hoofdstuk 7. Van een deelverzameling V van de verzameling N van alle natuurlijke getallen veronderstellen.

Programmeermethoden. Pointers. Walter Kosters. week 10: november kosterswa/pm/

Vakgroep CW KAHO Sint-Lieven

Getallensystemen, verzamelingen en relaties

1 Rekenen in eindige precisie

Derde college algoritmiek. 23 februari Complexiteit Toestand-actie-ruimte

10. Controleopdrachten

EE1400: Programmeren in C BSc. EE, 1e jaar, , 2e college

Recursie. public static int faculteit( int n ){ // Deze functie berekent n! // pre: n = N en n >= 0 // post: faculteit = N!

Optimalisatie technieken

OEFENINGEN PYTHON REEKS 1

start -> id (k (f c s) (g s c)) -> k (f c s) (g s c) -> f c s -> s c

Discrete Structuren. Piter Dykstra Opleidingsinstituut Informatica en Cognitie

Functies. Huub de Beer. Eindhoven, 4 juni 2011

Meer Blokken. 1. Dit is een functie genaamd Maximum, die twee argumenten heeft: number1 en number2.

Algoritmiek. 2 februari Introductie

Vierde college algoritmiek. 1 maart Toestand-actie-ruimte Brute Force

Uitleg van de Hough transformatie

Modelleren en Programmeren

Zevende college Algoritmiek. 6 april Verdeel en Heers

[14] Functies. Volg mee via 14_Functies-1.py. We beginnen met een eenvoudig voorbeeldje:

Modelleren en Programmeren

Arrays in LOGO. In LOGO heeft de eerste item van de array standaard index 1.

Datastructuren: stapels, rijen en binaire bomen

College Notatie, Recursie, Lijsten

Combinatoriek groep 1

Small Basic Programmeren Text Console 2

Combinatoriek groep 1 & 2: Recursie

Een korte samenvatting van enkele FORTRAN opdrachten

TEST INFORMATICA 1STE BACHELOR IN DE INGENIEURSWETENSCHAPPEN - ACADEMIEJAAR

Combinatoriek groep 2

VOORBLAD SCHRIFTELIJKE TOETSEN

REEKS I. Zaterdag 6 november 2010, 9u

Lineaire data structuren. Doorlopen van een lijst

Zevende college algoritmiek. 23/24 maart Verdeel en Heers

Uitwerking tentamen Algoritmiek 9 juli :00 13:00

Informatica. Deel II: les 1. Java versus Python. Jan Lemeire Informatica deel II februari mei Parallel Systems: Introduction

Week 5 : Hoofdstuk 11+ extra stof: meer over functies. Hoofdstuk 11:

Derde college algoritmiek. 22 februari Complexiteit Toestand-actie-ruimte

Zelftest Inleiding Programmeren

3 Modulorekenen. 3.1 De eulerfunctie en de kleine stelling van Fermat. Oefening 3.1. Bepaal Φ(1992), Φ(2011) en Φ(2048) (83 en 2011 zijn priem).

Eerste college algoritmiek. 5 februari Introductie

Python. Vraag 1: Expressies en types. Vraag 1 b: Types -Ingebouwde functies- Vraag 1 a 3/10/14

Tweede college algoritmiek. 12 februari Grafen en bomen

Zevende college algoritmiek. 24 maart Verdeel en Heers

11. Eenvoudige programma s schrijven in Maxima

OEFENINGEN PYTHON REEKS 1

Programmeermethoden NA

Toetsvoorbereiding Informatica HAVO/VWO 5 (T51) Programmeren met Python II. Duur: 70 minuten Datum: sept 2018

Oefening 4.3. Zoek een positief natuurlijk getal zodanig dat de helft een kwadraat is, een derde is een derdemacht en een vijfde is een vijfdemacht.

Jörg R. Hörandel Afdeling Sterrenkunde.

Informatica: C# WPO 11

Algoritmiek 2013/ verwijderen. BZboom: verwijderen. Complexiteit (= tijdcomplexiteit) van een algoritme: Algoritmiek 2013/04

Tentamen Imperatief en Object-georiënteerd programmeren in Java voor CKI

Variabelen en statements in ActionScript

Oefening 4.3. Zoek een positief natuurlijk getal zodanig dat de helft een kwadraat is, een derde is een derdemacht en een vijfde is een vijfdemacht.

Zoemzinnen. Algemene info. Functies met een variabel aantal argumenten

Pascal uitgediept Data structuren

Datastructuren en algoritmen

Een diverse inhoud deze keer: aangepaste procedures voor GIOS, machinetaal, geheugenmanipulatie, BASIC-equivalenten en recursief programmeren.

Hoofdstuk 1. Week 3: Recursie. 1.1 Leesopdracht. 1.2 Eenvoudige recursie

OPLOSSINGEN VAN DE OEFENINGEN

Programmeren met Arduino-software

Transcriptie:

10 Meer over functies In hoofdstuk 5 hebben we functies uitgebreid bestudeerd. In dit hoofdstuk bekijken we drie andere aspecten van functies: recursieve functies dat wil zeggen, functies die zichzelf direct of indirect aanroepen, functiepointers en functies met een variabel aantal argumenten. 10.1 Recursieve functies Zoals we al in hoofdstuk 5 hebben gezien, kan een functie elke andere functie aanroepen. Programmeurs maken van deze mogelijkheid gebruik om programma's hiërarchisch op te bouwen, waarbij de functie main subfuncties F1, F2,... aanroept om deeltaken te laten uitvoeren; deze subfuncties roepen weer subfuncties Gl, G2,... aan om nog eenvoudiger taken te laten uitvoeren, enzovoort (zie de hoofdstukken 5 en 7). Een met functionele decompositie gemaakt programmaontwerp kan dankzij de mogelijkheid van elkaar aanroepende functies rechtstreeks worden geïmplementeerd. Dit is niet de enige manier om functies te gebruiken. Een functie kan ook zichzelf aanroepen. Zo'n functie noemen we dan recursief. Voor veel programmeerproblemen bestaat een oplossing die met directe of indirecte recursie kan worden geformuleerd. Recursieve oplossingen zijn in het algemeen elegant en sluiten op een natuurlijke manier aan op het probleem. Recursie wordt vaak gebruikt in toepassingen waarin de oplossing kan worden geformuleerd in termen van het successievelijk toepassen van dezelfde oplossing op delen van het oorspronkelijke probleem. Dat komt vaak voor bij zoeken sorteerproblemen met betrekking tot recursief gedefinieerde datastructuren (zie de hoofdstukken 11, 12, 15 en 16). Vaak is een recursieve oplossing een alternatief voor een iteratieve. Laten we eens een functie bekijken die de faculteit van een getal berekent. De faculteit van een positieve integer n, geschreven als n!, is gedefinieerd als het produkt van de gehele getallen van 1 tot en met n. De faculteit van nul wordt als speciaal geval behandeld en is per definitie gelijk aan 1. We hebben dus: n! = n* (n - 1) * (n - 2) *... *3*2*1 voor n > 1 en 0! = 1 Dus: 5! = 5*4*3*2*1 = 120

De iteratieve versie van een functie voor de faculteit is: long int faculteit(int n) { int k; long produkt = 1L; if (n == 0) return (1L); else { for (k = n; k > 0; k--) produkt *= k; return (produkt); De definitie van de faculteitsfunctie wordt meestal recursief gegeven. We zien dat we n! = n* (n - 1) * (n - 2) *... *3*2*1 kunnen groeperen als n! = n* [ (n - 1) * (n - 2) *... *3*2*1] De groep tussen vierkante haken is natuurlijk gelijk aan de definitie van (n - 1)!. De recursieve definitie is daarom: n! = n*(n - 1)! met het speciale geval 0! = 1 We kunnen nu een functie ontwikkelen die de faculteit van een getal volgens deze recursieve definitie berekent. Programma 10-1 Geeft een tabel van faculteiten voor 0, 1, 2,... 10. De faculteiten worden met een recursieve functie berekend.

#include <stdio.h> main() { int j; long int faculteit(const int); for (j = 0; j <= 10; j++) printf("%2d! is %ld\n, j, faculteit(j)); long int faculteit(const int n) { if (n == 0) return (1L); else return (n * faculteit(n-1)); De uitvoer van het programma is: 0! is 1 1! is 1 2! is 2 3! is 6 4! is 24 5! is 120 6! is 720 7! is 5040 8! is 40320 9! is 362880 10! is 3628800 De functie faculteit is recursief omdat er een aanroep van de functie zelf in voorkomt. Laten we eens bekijken wat er gebeurt als de functie wordt aangeroepen om de faculteit van 5 te berekenen. Als de functie wordt binnengegaan, wordt de formele parameter n gelijk gemaakt aan 5. De if-statement stelt vast dat n niet nul is en levert de waarde af die wordt verkregen door het evalueren van n * faculteit (n - l) met n = 5,en dat is: 5 * faculteit (4) Deze expressie geeft aan dat de functie faculteit nog eens moet worden aangeroepen, dit keer om de waarde van faculteit(4) te berekenen. De vermenigvuldiging met 5 wordt uitgesteld tot faculteit (4) is berekend. We roepen de functie faculteit dus nog eens aan. Het actuele argument is nu 4. Elke keer dat een functie in C wordt aangeroepen, wordt er een eigen stel automatische variabelen en formele parameters toegewezen om mee te werken. Dat geldt ook voor recursieve functies. Het formele argument n dat bestaat als de functie wordt aangeroepen om de faculteit van 4 te

berekenen, verschilt van het formele argument uit de eerste aanroep van de functie met argument 5. Nu n de waarde 4 heeft, levert de functie de waarde van de expressie 4 * faculteit (3) af. De vermenigvuldiging met 4 wordt weer uitgesteld tot de faculteit van 3 is berekend. Dit gaat zo door tot het formele argument de waarde 0 heeft. Dan hebben we de situatie die u in tabel 10-1 ziet. faculteiten return (n * faculteit (n-1)) 5 5 * faculteit (4) = 5 *? 4 4 * faculteit (3) = 4 *? 3 3 * faculteit (2) = 3 *? 2 2 * faculteit (1) = 2 *? 1 1 * faculteit (0) = 1 * 0 Tabel 10-1 Als het formele argument n tot 0 is gereduceerd, laat de if -statement de functie onmiddellijk de long waarde 1 afleveren. De recursieve afdaling kan nu weer omhoog gaan en alle uitgestelde vermenigvuldigingen kunnen in omgekeerde volgorde worden uitgevoerd. Als we tabel 10-1 omkeren, krijgen we tabel 10-2. faculteit (n) return (n * faculteit (n-1)) Tabel 10-2 1 1 * faculteit (0) = 1 * 1 = 1 2 2 * faculteit (1) = 2 * 1 = 2 3 3 * faculteit (2) = 3 * 2 = 6 4 4 * faculteit (3) = 4 * 6 = 24 5 5 * faculteit (4) = 5 * 24 = 120 Voor wie dit misschien een wat kunstmatig voorbeeld vindt, bekijken we nu de algoritme van Euclides voor het bepalen van de grootste gemene deler van twee positieve gehele getallen m en n. De algoritme kan zo worden beschreven:

GGD (n, m) = als m > n: GGD (m, n) als m = 0: n anders: GGD(M, rest na deling van n door m) De recursieve functie kan rechtstreeks uit deze beschrijving worden afgeleid: int ggd(int n, int m) { if (m > n) return (ggd(m, n)); else if (m == 0) return (n); else return (ggd(m, n % m)); Voor een niet-recursieve oplossing is enige vaardigheid bij het programmeren nodig. De implementatie is ook minder duidelijk: int ggd(int n, int m) { if (m > n) { int hulp = m; m=n; n=hulp; while (n % m!= 0) { int hulp = m; m=n % m; n=hulp; return (m); Het is in C ook mogelijk een functie te schrijven die een tweede functie aanroept die weer de oorspronkelijke functie aanroept. Dan hebben we een cyclus van functieaanroepen en spreken we van indirecte recursie. We kunnen bijvoorbeeld drie functies A, B en C hebben, waarbij A de functie B, B de functie C, en C de functie A aanroept. Om het mogelijk te maken dat een aanroep van een functie voorafgaat aan de declaratie van die functie, moet er een prototypedeclaratie worden gebruikt. Bij indirecte recursie is dat nodig, omdat het programma

nooit zo kan worden ingedaald dat elke functiedeclaratie aan het gebruik van de functie voorafgaat. Neem bijvoorbeeld twee elkaar aanroepende functies A en B. Omdat A de functie B aanroept, kunnen we B eerder dan A plaatsen, maar dan wordt A eerder aangeroepen dan gedeclareerd. Recursie is niet altijd de efficiëntste oplossing voor een probleem. Veel problemen die recursief kunnen worden opgelost, kunnen ook met iteratie worden opgelost, zoals we hebben gezien. De oplossing is dan misschien minder elegant, maar wel efficiënter in termen van de uitvoeringstijd van het programma en het benodigde geheugen. Voor elke recursieve functieaanroep wordt een apart stuk geheugen in gebruik genomen om de waarde van de argumenten en de lokale variabelen in op te slaan. Daardoor zijn recursieve functies duurder in geheugenruimte. Elke recursieve functieaanroep vergt ook tijd voor het doorgeven van de argumenten, het in gebruik nemen van nieuw geheugen en het afleveren van het resultaat. Het overtuigendste argument ten gunste van recursieve functies is dat ze een goede afspiegeling zijn van recursief gedefinieerde datastructuren en algoritmen die recursief gedefinieerd zijn. Verder zijn sommige recursieve algoritmen vrijwel niet iteratief te schrijven. Recursieve functies die recursieve datastructuren weerspiegelen, zijn in overeenstemming met onze filosofie van programma's die met data overeenkomen. Een goede oefening in het schrijven van recursieve functies is het probleem van de toren van Hanoi. Gegeven zijn drie pennen en een aantal (64) schijven. Deze schijven hebben allemaal een andere diameter. De beginsituatie is die waarbij alle schijven op 1 pen (bijvoorbeeld pen 1) liggen. Hierbij liggen er alleen kleinere schijven boven een grotere. De eindsituatie is die waarbij alle schijven op pen 3 liggen, waarbij ook weer eveneens nooit een grotere pen boven een kleinere ligt. Om het probleem op te lossen moeten monniken schijf per schijf verplaatsen. Er moet voldaan zijn aan de voorwaarde dat alleen de verplaatste schijf (eventjes) niet op een pen mag liggen: alle andere schijven moeten steeds op een pen liggen. De monniken mogen ook steeds maar 1 schijf verplaatsen: de schijven wegen te zwaar om er meerdere te verplaatsen. Tijdens de bewerking moet er steeds voldaan zijn aan de voorwaarde dat er nooit een grotere schijf boven een kleinere mag liggen. Om nog een klein beetje een overzicht op de bewerking te hebben, is het beter het aantal schijven variabel te houden en dit in het begin zeer klein te kiezen, bijvoorbeeld 3 of 4. Wanneer men de PC het probleem met 64 schijven laat oplossen zal het zeer lang duren!!!