Lidt mere om funktioner
- skrev denne artikel Torsdag, 20 Juli 2006
Forudsætninger: Jeg går ud fra du har læst introduktionen og at du har kendskab til løkker og arrays. Desuden er det nok smart at læse om funktioner før du læser mere om dem ikke sandt?
Vi har kigget lidt på funktioner allerede så jeg vil bare beskrive nogle ekstra ting man kan med funktioner.
Det første jeg vil vise er overload, dvs. flere funktioner der hedder det samme. Dette er for så vidt tilladt, men de skal i så fald have forskellige argumenter (variable der bliver sendt med til funktionen). På den måde kan man så styre hvilken en der blev kaldt f.eks.
001   void DBox(char *msg);
002   void DBox(int i);
003  
004   void DBox(char *msg){
005    MessageBox(0,msg,"Debug",MB_OK);
006   }
007  
008   void DBox(int i){
009    char msg[32];
010    wsprintf(msg,"%d",i);
011    DBox(msg)
012   }

Dette giver en debug MessageBox funktion der både kan skrive ints og almindelige strenge ud. Jeg har her valgt at benytte at bruge den første funktion i den anden fordi det er smart hvis jeg senere vil have debug beskeder i consollen eller en fil i stedet (på den her måde skal der kun rettes et sted).
Det er tilladt at lade dem returnere værdier af forskellige typer men som regel er det smart at holde dem på det samme og især er det smart ikke lige pludselig at lade dem virke helt forskelligt. Det er dog ikke tilladt at have en overloadet funktion der kun adskiller sig ved det den returnerer. Dette er der mange årsager til og en af de mest ligetil er at man ikke behøver samle en funktions returværdi op i en variabel (MessageBox returner hvad knap der er trykket på, men når der kun er en ok knap er vi ret ligeglade så den plejer vi ikke at tage imod, hvis der var en anden MessageBox funktion f.eks. en der returnerede en streng om hvad der stod på knappen der blev trykket på ville compileren ikke vide hvilken af de 2 MessageBox funktioner der skulle bruges og den har ingen garanti for at de rent faktisk gør det samme).
Jeg vil også beskrive rekurs. Rekurs er et almindeligt programmeringsprincip. Det går ud på at man har en funktion der kalder sig selv. Naturligvis skal man sikre sig at den ikke bare altid kalder sig selv men at den stopper på et tidspunkt. Et eksempel på et sted hvor det er godt at bruge rekurs er hvis man skal løse The Tower of Hanoi er lille spil hvor man har f.eks. 4 ringe i forskellig størrelse stablet på et tårn, derudover har man 2 tomme tårne, nu skal man så flytte ringene fra det ene tårn til det andet 1 af gangen men man må ikke flytte en mindre ring oven på en større (Hvis det ikke giver mening kan du slå det op på nettet men der er en god sandsynlighed for at du har set det før). Vi forestiller os nu at vi skal løse problemet for 3 ringe fra pind A til pind C via pind B. Dette virker meget uoverskueligt lige umild bart, men hvis vi nu havde et trick til at flytte alle ringene bortset fra den nederste til pind B så kunne vi jo flytte den største over på pind C og så flytte dem fra B oven på, på samme måde bare med pind A som mellempind denne gang. Nu skal vi så bare have fundet ud af at flytte 2 brikker, men vi beslutter os igen for at det ikke er til at overskue. Så i stedet beslutter vi os for at hvis de skal rygges til pind B må vi først have flyttet alle undtagen den nederste af de 2 ringe som vi nu er ved at flytte over på pind C, så kan vi flytte ring 2 på pind B og flytte alle de ringe vi flyttede over på pind C tilbage på pind B men via pind A som jo så stadig har en ring på men den er jo heldigvis større end alle de andre ringe vi kigger på så den går ikke i vejen. Hvis det ikke er gået op for dig at alle undtagen den nederste af de 2 ringe er det samme som den øverste ring så start forfra. Vi har altså nu fundet ud af at vi kan flytte 2 brikker over på pind B via pind C som følge flyt ring 1 fra pind A til pind C, flyt ring 2 fra pind A til pind B og flyt ring 1 fra pind C til pind B. Nu er vi så tilbage til der hvor vi stadig skulle flytte 3 ringe men hvor vi har flyttet 2. Så nu flytter vi ring 3 fra pind A til pind C. Her havde vi så besluttet at vi nu skulle flytte ringene fra pind B til pind C via pind A som jo nu er tom. Vi har vist at vi kunne flytte 2 ringe ved først at flytte alle undtagen den nederste (det er faktisk det samme som den øverste) til via pinden, derefter flytter vi så den nederste på pind B over på pind C som nu har ring 2 og 3 på sig. Nu flytter vi jo så bare ring 1 over på pind C og så er vi færdige. Der var selvfølgelig en bagtanke med at vise løsningen for 3, for som vi så kunne vi løse problemet med 3 MEGET nemt hvis vi bare kunne klare at flytte 2. Derefter så vi at 2 ikke var et problem fordi vi NEMT kunne flytte 2 hvis vi bare kunne flytte 1. Faktum er at 10000 ikke er et problem hvis vi kan flytte 9999 (hvilket vi ikke kan!). Vi skal bare flytte alle sammen undtagen 1 til via pinden og så flytte den sidste over på til pinden og så alle dem fra via pinden til, til pinden. Dette kan vi skrive som kode på følgende måde:
001   void Hanoi(int antal,char fra,char via,char til){
002    if(antal>0){
003      Hanoi(antal-1,fra,til,via);
004      Flyt(fra,til);
005      Hanoi(antal-1,via,fra,til);
006    }
007   }

Om man så vil lave en flyt funktion der smider MessageBoxe i hovedet på brugeren eller vil gemme til en fil er ligegyldigt. Vær dog opmærksom på at 3 brikker krævede 7 flyt. 4 ville kræve 15 og 10 ville kræve 1023 flyt. Dette gør The Tower of Hanoi til et ret langt spil med bare et moderat antal brikker og med 20 og derover er det et problem at løse selv for en computer da mængden af træk så er over 1 million (1048575 for nøjagtigheds freaks, 2n-1 for de lidt mere matematiske). Hvis man vælger over 5 bør man benytte sig af at gemme til fil da det er rimeligt trist at side og klikke på ok i MessageBox i flere minutter.
Et andet sted hvor rekurs er godt er ved floodfill, den algoritme kan du prøve at skrive selv og ellers tager vi den en anden gang. Som altid: Hvis der er nogen spørgsmål så send mig en mail på:admin@AODASoft.net