4. Funktioner
Programmeringsteknik
Versionen från 25 juni 2007 kl. 11.21 (redigera) KTH.SE:u1xsetv1 (Diskussion | bidrag) ← Gå till föregående ändring |
Nuvarande version (10 december 2010 kl. 01.07) (redigera) (ogör) KTH.SE:u1h4cq58 (Diskussion | bidrag) (→Inlämningsuppgift 2 (Rondelet)) |
||
(66 mellanliggande versioner visas inte.) | |||
Rad 1: | Rad 1: | ||
- | ==Filer== | + | ==Vad har man funktioner till?== |
- | <p>Information som man vill ha kvar på en dator sparas på fil. Det kan t ex röra sig om ett brev som har skapats med hjälp av ett ordbehandlingsprogram, en bild som har skapats av ett ritningsprogram eller en kamera osv. Filer sparas på långtidsminnen som t ex datorns skivminne (hårddisken). När man sparat en fil på hårddisken kommer filen att finnas kvar där även när man har startat om datorn eller t ex när det blir strömavbrott. Därför är det bra att spara den programkod man skriver på en fil för senare bearbetning. Dels för att det sällan blir rätt första gången, dels för att när man väl provkört kommer man på fler saker man önskar av programmet. </p> | + | Stora program är besvärligare att konstruera än små. |
+ | Men man kan göra det enklare för sig genom att dela | ||
+ | upp programmet i funktioner. En funktion är som ett | ||
+ | litet delprogram i programmet. | ||
- | <p>Filer delas upp i tre sorter:</p> | + | [[Bild:Snigeltavling.jpg]] |
- | <p><ol> | + | När man anordnar tävlingar med riktiga sniglar tar det mycket |
- | <li>Vanliga filer: som innehåller data (t ex text eller bildinformation). | + | lång tid innan deltagarna kommer i mål. Vi ska istället skriva |
- | <li>Katalog eller mapp: som innehåller andra filer. | + | ett litet program som simulerar en tävling mellan två sniglar: |
- | <li>Länkar, genvägar eller alias: En fil som refererar till en fil. | + | |
- | </ol></p> | + | |
- | <p>Kataloger används för att organisera filer och program. Kataloger kan innehålla andra kataloger. T ex kan man spara alla filer som har skapats i samband med sommarkursen under katalogen ”sommarkurs”. Strukturen kallas i datorsammanhang ofta för ett filträd.</p> | + | <pre> |
+ | --------------------------------------------------- | ||
+ | | VEM HAR DEN SNABBASTE SNIGELN? | | ||
+ | | | | ||
+ | | Här får din snigel tävla mot en vältränad | | ||
+ | | racersnigel. Skriv in namnet på din snigel | | ||
+ | | så sätter tävlingen igång! | | ||
+ | --------------------------------------------------- | ||
+ | Vad heter din snigel? Ebba | ||
+ | Klara...färdiga...gå! | ||
- | <p>Länkar används t ex för att man ska kunna hitta samma fil på flera ställen. En bildfil kan ta stor plats och en populär bild kan man vilja ha med på många ställen. För att slippa kopiera bilden lägger man då istället länkar till den. Länkarna tar nästan ingen plats alls.</p> | + | Ebba: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @ |
+ | Racersnigeln: - - - - - - - - - - - - - - - - - - - - - - - - - - - @ | ||
- | ==Filhantering== | ||
- | <p>På nästan alla datorer finns det ett grafiskt användargränssnitt (det du ser på skärmen) som kan användas för att organisera filer mha musen, kortkommandon eller ett filhanteringsprogram.</p> | + | Det här loppet tog en oväntad vändning, Ebba vann! |
+ | </pre> | ||
- | <p>Filer, kataloger och länkar har då grafiska symboler som kallas för ikoner. Följande bilder visar hur olika ikoner ser ut i ett Mac/OS X operativsystem:</p> | ||
- | [BILD] [BILD] [BILD] | + | ==Dela upp ett problem i funktioner== |
- | <p>I programmeringssammanhang är det väldigt vanligt att använda tangentbordskommandon för filhanteringen, t ex för att utföra:</p> | + | För att programmet ska bli enklare att skriva bestämmer vi |
+ | först vilka funktioner vi ska ha. Det är inte alls självklart | ||
+ | hur programmet ska delas upp i funktioner; samma problem | ||
+ | kan lösas på många olika sätt! Vi vill se till att varje | ||
+ | funktion har ett enda uppdrag. | ||
- | <p><ol> | + | Här är ett förslag på uppdelning: |
- | <li>Förflyttning i filträdet. | + | |
- | <li>Kopiering av filer. | + | |
- | <li>Radering av filer. | + | |
- | </ol></p> | + | |
- | <p>Ofta är den grafiska filhanteringen enklare, men i programmeringssammanhang vill man använda tangentbordskommandon så mycket som möjligt eftersom det går snabbare (när man väl kan det).</p> | + | # Skriv ut informationsrutan och läs in namnet på snigeln |
+ | # Simulera tävlingen och rita upp banorna | ||
+ | # Skriv ut vem som vann | ||
- | ==Förflyttning i filträd== | + | Ettan och tvåan består av flera olika moment. Vi gör det |
+ | ännu enklare för oss genom att dela upp i ännu mindre delar: | ||
- | <p>Tangentbordskommandon använder man i ett terminalfönster. För att öppna ett terminalfönster på en Windowsdator: tryck först på windows-tangenten, håll den nere och tryck därefter på R. I det fönster som öppnas, skriv in CMD och tryck retur. Har du Macintosh OS X kan du se Python-tolk-instruktionerna i avsnitt 1.9 om villkor om du glömt hur man öppnar ett sådant. Vi börjar med kommandot cd (som står för <b>c</b>hange <b>d</b>irectory - byt katalog) används för att förflytta sig i filträdet.</p> | + | # Skriv ut informationsrutan |
+ | # Läs in namnet på en snigel | ||
+ | # Simulera tävlingen | ||
+ | # Rita upp en snigels bana | ||
+ | # Skriv ut vem som vann | ||
- | <p>Antag att man befinner sig i katalogen "sommarkurs" och man vet att i katalogen finns en annan katalog med namnet "test". Då kan man skriva följande kommando för att flytta sig till katalogen "test":</p> | + | Vi börjar med att skriva in dessa moment som kommentarer i |
+ | filen snigel.py, för att vi ska hålla reda på vad som | ||
+ | är kvar att göra. Då får vi också en kommentar för varje | ||
+ | funktion, så att det syns vad som händer var. | ||
- | <div class="codebox"><div class="title">Terminalfönster</div>>cd test</div> | + | [[Bild:Books.jpg|thumb]] |
+ | '''Fråga: Tänk om vi ville skriva ett program för stavningskontroll, i vilka funktioner skulle vi då dela upp det?''' | ||
- | <p>Det finns två speciella namn "." och ".." som har stor betydelse för systemet.</p> | + | '''Svar:'''<span style="color:white">Till exempel följande: inläsning av ordlistan, kontroll av ett ord mot ordlistan, utskrift av rättelseförslag </span> |
- | <p>"." betyder aktuella katalogen.</p> | + | ==Definiera en funktion== |
- | <p>".." betyder katalogen som innehåller aktuella katalogen (dvs katalogen ovanför den aktuella katalogen).</p> | + | Först kommer funktionshuvudet (första raden i funktionen). Exempel: |
- | <p>"." och ".." kommer att automatiskt finnas i varje katalog.</p> | + | <pre> |
+ | def visaInformation(): | ||
+ | </pre> | ||
- | <p>Vad begreppet "aktuell katalog" betyder brukar framgå efter ett tags användning, men en analogi är rum i ett hus. Om du går in i ett rum, t ex ditt sovrum så är sovrummet det aktuella rummet. Räknar du upp alla möbler där, så får du ett annat resultat än om du räknar upp möblerna i t ex köket. En nivå upp (katalogen ovanför) kan du jämställa med huset (om du tänkte på en villa) eller lägenheten. En nivå ner (en katalog i rummet) skulle kunna motsvaras av en garderob. I en dator finns det inga begränsningar på antalet nivåer man kan ha, men det gör det förstås i verkligheten.</p> | + | [[Bild:Huvud.jpg|thumb|left]] |
+ | Funktionshuvudet består av fyra delar. | ||
+ | * Ordet def (talar om att en funktion ska definieras här) | ||
+ | * Funktionens namn, gärna ett verb som beskriver vad funktionen gör | ||
+ | * Ett par parenteser () | ||
+ | * Ett kolon | ||
- | <p>I samband med kommandot "cd" förekommer ofta två andra viktiga kommandon "ls" och "pwd". Kommandot "ls" (som står för list, lista) eller "dir" i dos används för att lista vilka filer finns i katalogen och "pwd" (som står för print working directory, skriv aktuellt katalognamn) (motsvarande pwd finns inte i dos, istället skrivs katalognamnet alltid ut) används för att ta reda på namn på den aktuella katalogen. Betrakta följande bild:</p> | + | Under funktionshuvudet kommer funktionskroppen, som helt enkelt är |
+ | en följd av indenterade satser som funktionen ska utföra. | ||
+ | Så här ser hela funktionen ut: | ||
- | [BILD] | + | <pre> |
+ | # Skriver ut informationsrutan | ||
+ | def visaInformation(): | ||
+ | print """ | ||
+ | --------------------------------------------------- | ||
+ | | VEM HAR DEN SNABBASTE SNIGELN? | | ||
+ | | | | ||
+ | | Här får din snigel tävla mot en vältränad | | ||
+ | | racersnigel. Skriv in namnet på din snigel | | ||
+ | | så sätter tävlingen igång! | | ||
+ | ---------------------------------------------------""" | ||
+ | </pre> | ||
- | <p>I fortsättningen visas användarens inmatning i fet stil, resten av texten skrivs av datorn. När den aktuella katalogen är sommarkurs och man skriver följande kommandon i tur och ordning får man dessa svar av datorn:</p> | + | (För att kunna skriva en sträng som sträcker sig över flera rader |
+ | så startar och avslutar vi strängen med tre stycken "-tecken. | ||
+ | Vanliga trängar, som börjar och slutar med enkelt | ||
+ | citattecken, kan nämligen inte innehålla radbrytningar.) | ||
- | <div class="codebox-divided"><div class="codecolumn1"><div class="title">Unix/Mac OS X</div><pre>>pwd | + | Lägg till koden ovan i din fil snigel.py och provkör. |
- | sommarkurs | + | |
- | >ls | + | |
- | programkod test | + | |
- | >cd programkod | + | |
- | >pwd | + | |
- | sommarkurs/programkod | + | |
- | >ls | + | |
- | Prog.py Prog2.py | + | |
- | >cd Prog.py | + | |
- | Prog.py: Not a directory | + | |
- | >cd .. | + | |
- | >pwd | + | |
- | sommarkurs | + | |
- | >cd test/Katalog1 | + | |
- | >pwd | + | |
- | sommarkurs/test/katalog1 | + | |
- | > </pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs>dir | + | |
- | 06-05-01 11.45 <DIR> programkod | + | |
- | 06-05-02 20:23 <DIR> test | + | |
- | C:\sommarkurs>cd programkod | + | |
- | C:\sommarkurs\programkod> dir | + | |
- | 06-06-01 <DIR> . | + | |
- | 06-06-01 <DIR> .. | + | |
- | 06-06-02 10.45 1024 PROG.PY | + | |
- | 06-05-11 11.11 2048 PROG2.PY | + | |
- | C:\sommarkurs\programkod> cd prog.py | + | |
- | The directory name is invalid | + | |
- | C:\sommarkurs\programkod> cd .. | + | |
- | C:\sommarkurs> cd test/Katalog1 | + | |
- | C:\sommarkurs\test\Katalog1></pre></div></div> | + | |
+ | Men vad nu - programmet skriver inte ut något alls? | ||
+ | Gå vidare till nästa avsnitt för att få reda på varför! | ||
- | <span style="color:red">[FRÅGOR]</span> | ||
+ | ==Anropa en funktion== | ||
- | ==Kopiera filer== | + | Satserna i funktionskroppen utförs inte om inte funktionen |
+ | anropas. Anropet ska stå längst ner i programmet, under | ||
+ | funktionsdefinitionerna. Den delen av programmet kallar vi | ||
+ | huvudprogrammet. | ||
- | <p>Kommandot cp (som står för copy, kopiera) eller copy i DOS används för att kopiera filer. Anta att aktuella katalogen är sommarkurs och vi vill kopiera filen Prog.py som finns i katalogen sommarkurs/programkod till katalogen Katalog1.</p> | + | Anropet består bara av funktionens namn följt av ett parentespar. |
+ | Om du lägger till anropet sist i ditt program ska alltihop se ut så här: | ||
- | <p>Efter kopieringen kommer vi att få följande filstruktur:</p> | + | <pre> |
+ | # Skriver ut informationsrutan | ||
+ | def visaInformation(): | ||
+ | print """ | ||
+ | --------------------------------------------------- | ||
+ | | VEM HAR DEN SNABBASTE SNIGELN? | | ||
+ | | | | ||
+ | | Här får din snigel tävla mot en vältränad | | ||
+ | | racersnigel. Skriv in namnet på din snigel | | ||
+ | | så sätter tävlingen igång! | | ||
+ | ---------------------------------------------------""" | ||
- | [BILD] | + | # Läs in namnet på en snigel |
+ | # Simulera tävlingen | ||
+ | # Rita upp en snigels bana | ||
+ | # Skriv ut vem som vann | ||
- | <div class="codebox-divided" style="width: 565px;"><div class="codecolumn1"><div class="title">Unix/Mac OS X</div><pre>>pwd | + | visaInformation() |
- | sommarkurs | + | </pre> |
- | >cp programkod/Prog.py test/Katalog1/ | + | |
- | ></pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs>copy programkod\Prog.py test\Katalog1\ | + | |
- | C:\sommarkurs></pre></div></div> | + | |
- | <p>Första kommandot pwd är för att säkerställa att vi är i rätt katalog, detta kommando är alltså inte nödvändigt om man vet att aktuella katalogen är sommarkurs.<br /> | + | Spara i filen snigel.py och provkör! |
- | Den andra kommandoraden består av följande tre delar:<br /> | + | |
- | Själva kommandot: cp (copy i dos)<br /> | + | |
- | Sökvägen till källan (dvs den fil man vill kopiera)<br /> | + | |
- | Sökvägen till destinationen (dvs den katalog man vill kopiera filen till)</p> | + | |
- | <p>Om man vill kopiera en hel katalog med alla filer den innehåller måste man även ange flaggan r , (i dos finns ett eget kommando för detta xcopy med flaggan /s får man nästan samma effekt). Anta att vi vill kopiera hela katalogen test med alla filer som finns i den till programkod. Detta gör vi med följande kommando om den aktuella katalogen är sommarkurs:</p> | ||
- | <div class="codebox-divided" style="_width: 480px;"><div class="codecolumn1"><div class="title">Unix/Mac OS X</div><pre>cp -r test programkod</pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs>xcopy /s test programkod</pre></div></div> | + | ==Skicka utdata från en funktion (returvärden)== |
- | <p>Efter kopieringen får vi följande struktur:</p> | + | [[Bild:Brev.jpg|right|Ett brev]] |
+ | Då ger vi oss på nästa funktion, som ska läsa in namnet på en snigel. | ||
+ | Namnet på snigeln är utdata från funktionen. Den som kör programmet | ||
+ | och knappar in namnet tycker förstås att det är indata, men | ||
+ | ur funktionens synvinkel är det nåt som ska skickas ut. | ||
- | [BILD] | + | För att skicka ut ett värde ur funktionen skriver vi ordet |
+ | return följt av den variabel vi vill skicka ut värdet på. | ||
+ | Funktionen som läser in namnet kommer att se ut så här: | ||
+ | <pre> | ||
+ | def lasNamn(): | ||
+ | namn = raw_input("Vad heter din snigel? ") | ||
+ | return namn | ||
+ | </pre> | ||
- | <span style="color:red">[FRÅGOR]</span> | + | När vi ska anropa funktionen måste vi ta hänsyn till att den |
+ | returnerar ett värde. Därför skriver vi anropet i en tilldelningssats, | ||
+ | med en variabel till vänster som får ta emot det returnerade värdet. | ||
+ | Variabeln behöver inte ha samma namn som variabeln i return-satsen | ||
+ | inuti funktionen. | ||
- | ==Radera filer== | + | <pre> |
- | <p><strong>Varning:</strong> Det finns en viktig skillnad mellan terminalfönsterbaserad filhantering och grafisk filhantering när det gäller borttagning av filer. När man tar bort en fil med hjälp av den grafiska filhanteraren kommer filen hamna i en speciell katalog som brukar kallas för papperskorg, alltså filen är inte riktigt borttagen från datorn (vill man ta bort filen så måste man tömma papperskorgen). När man använder terminalfönsterbaserad filhantering försvinner alla filer som tas bort för gott. Därför ska man vara väldigt försiktig när man ta bort filer i terminalfönstret.</p> | + | dinSnigelsNamn = lasNamn() |
+ | </pre> | ||
- | <p><span class="code">rm</span> (som står för <b>r</b>e<b>m</b>ove) eller del i DOS är kommandot som används för att ta bort filer. Anta att aktuella katalogen är sommarkurs och man vill ta bort filen <span class="code">Prog2.py</span>. Följande kommando tar bort filen <span class="code">Prog2.py</span>:</p> | + | När den här satsen utförs av Python kommer följande att hända. Först |
+ | anropas funktionen lasNamn, som läser in namnet och returnerar det. | ||
+ | Sen kommer det returnerade värdet att lagras i variabel dinSnigelsNamn. | ||
- | <div class="codebox-divided" style="_width:470px;"><div class="codecolumn1"><div class="title">Unix/Mac OS X</div><pre>>rm programkod/Prog2.py | + | Vi lägger in den nya i filen snigel.py och provkör. |
- | ></pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs>del programkod\prog2.py | + | |
- | C:\sommarkurs></pre></div></div> | + | |
- | <p>Så här kommer filstrukturen se ut efter borttagningen:</p> | + | <pre> |
+ | # Skriver ut informationsrutan | ||
+ | def visaInformation(): | ||
+ | print """ | ||
+ | --------------------------------------------------- | ||
+ | | VEM HAR DEN SNABBASTE SNIGELN? | | ||
+ | | | | ||
+ | | Här får din snigel tävla mot en vältränad | | ||
+ | | racersnigel. Skriv in namnet på din snigel | | ||
+ | | så sätter tävlingen igång! | | ||
+ | ---------------------------------------------------""" | ||
- | [BILD] | + | # Läs in namnet på en snigel |
+ | def lasNamn(): | ||
+ | namn = raw_input("Vad heter din snigel? ") | ||
+ | return namn | ||
- | <p>Om man vill ta bort en hel katalog med alla filer i ska man i UNIX-baserade operativystem återge flaggan r till kommandot <span class="code">rm</span>.</p> | + | # Simulera tävlingen |
+ | # Rita upp en snigels bana | ||
+ | # Skriv ut vem som vann | ||
- | <p>I Windows är det däremot annorlunda - kommandot <span class="code">del</span> som vi använde oss av ovan fungerar bara på filer, inte mappar. Istället är kommandot <span class="code">rmdir</span> det som gäller. Om mappen inte är tom måste man även ange flaggan S.</p> | + | visaInformation() |
+ | dinSnigelsNamn = lasNamn() | ||
+ | </pre> | ||
- | <p>Antag att den aktuella katalogen är sommarkurs och man vill ta bort katalogen test som finns under katalogen programkod med följande kommando:</p> | + | Men hur ska vi veta om det fungerade eller inte? |
+ | Jo, vi lägger in en kontrollutskrift | ||
+ | <pre> | ||
+ | print "Din snigel heter alltså", dinSnigelsNamn | ||
+ | </pre> | ||
+ | allra sist i programmet. Kontrollutskrifter är ett enkelt sätt | ||
+ | att se om programmet fungerar som det ska, och är till stor | ||
+ | nytta när man försöker ta reda på varför programmet inte gör | ||
+ | som det ska. | ||
- | <div class="codebox-divided"><div class="codecolumn1"><div class="title">Unix/Mac OS X</div><pre>>rm -r programkod/test | + | Provkör igen. När programmet fungerar kan du ta bort kontrollutskriften! |
- | ></pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs>rmdir /S programkod\test<br>C:\sommarkurs></pre></div></div> | + | |
- | <p>Bilden nedan visar filstrukturen efter borttagning:</p> | + | Det här är viktigt, så vi tar ett exempel till, den här gången med |
+ | två returvärden. Nästa funktion ska simulera tävlingen, på det | ||
+ | här viset: | ||
+ | * Sniglarna startar bägge på startstrecket, dvs vid 0 | ||
+ | * Vi skriver ut Klara...färdiga...gå | ||
+ | * Och startar själva tävlingen, som representeras av en while-slinga | ||
+ | * I varje varv i slingan låter vi sniglarna ta varsitt skutt av slumpmässig längd | ||
+ | * När någon av sniglarna (eller bägge) passerar mållinjen så avbryter vi | ||
+ | * Sist returnerar vi bägge sniglarnas slutpositioner | ||
- | [BILD] | + | <pre> |
+ | def tavling(): | ||
+ | snigelbana1 = 0 | ||
+ | snigelbana2 = 0 | ||
+ | print "Klara...färdiga...gå! \n" | ||
+ | while snigelbana1 < DISTANS and snigelbana2 < DISTANS: | ||
+ | snigelbana1 += random.randrange(5) | ||
+ | snigelbana2 += random.randrange(5) | ||
+ | return snigelbana1, snigelbana2 | ||
+ | </pre> | ||
- | <span style="color:red">[FRÅGOR]</span> | + | I return-satsen allra sist kan vi se att två värden |
+ | returneras, åtskilda med kommatecken. Givetvis måste vi då också | ||
+ | ta emot två värden i huvudprogrammet. | ||
- | ==Flytta filer== | + | Den distans (i centimeter) som sniglarna ska tillryggalägga |
+ | representeras av konstanten DISTANS. En variabel som ska | ||
+ | ha ett konstant värde under hela programmet brukar man | ||
+ | skriva med stora bokstäver, VERSALER. Python bryr sig inte om det, men | ||
+ | andra människor som läser programmet kan se vad man menar. | ||
+ | Vi definierar DISTANS överst i huvudprogrammet, och frågar | ||
+ | oss om den kommer att synas inuti funktionen? | ||
- | <p>Om man vill flytta en fil från en katalog till en annan katalog kan man använda sig av kommandot <span class="code">mv</span> (som står för move) eller <span class="code">move</span> i DOS. Anta nu att den aktuella katalogen är sommarkurs och man vill flytta filen <span class="code">prog.py</span> från katalogen <span class="code">programkod</span> till katalogen <span class="code">test</span>. Detta görs med hjälp av följande kommandorad:</p> | + | Lägg in den nya funktionen i snigel.py, enligt nedan. Glöm inte |
+ | att lägga in kontrollutskrifter av snigelbana1 och snigelbana2 | ||
+ | innan du provkör! | ||
- | <div class="codebox-divided" style="_width: 500px;"><div class="codecolumn1"><div class="title">Unix/Mac OS X</div><pre>>mv programkod/Prog.py test/<br>></pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs>move programkod\prog.py test<br>C:\sommarkurs></pre></div></div> | + | <pre> |
+ | import random | ||
- | <p>Ovanstående kommando består av tre följande delar:</p> | + | # Skriver ut informationsrutan |
+ | def visaInformation(): | ||
+ | print """ | ||
+ | --------------------------------------------------- | ||
+ | | VEM HAR DEN SNABBASTE SNIGELN? | | ||
+ | | | | ||
+ | | Här får din snigel tävla mot en vältränad | | ||
+ | | racersnigel. Skriv in namnet på din snigel | | ||
+ | | så sätter tävlingen igång! | | ||
+ | ---------------------------------------------------""" | ||
- | <p><ol> | + | # Läser in namnet på användarens snigel |
- | <li>Själva kommandot: <span class="code">mv</span> | + | def lasNamn(): |
- | <li>Hela sökvägen till källan: <span class="code">programkod/prog.py</span> | + | namn = raw_input("Vad heter din snigel? ") |
- | <li>Hela sökvägen till destinationen: <span class="code">test/</span> | + | return namn |
- | </ol></p> | + | |
- | <p>Så här ser fil-strukturen ut:</p> | + | # Simulerar en tävling mellan två sniglar |
+ | def tavling(): | ||
+ | snigelbana1 = 0 | ||
+ | snigelbana2 = 0 | ||
+ | print "Klara...färdiga...gå! \n" | ||
+ | while snigelbana1 < DISTANS and snigelbana2 < DISTANS: | ||
+ | snigelbana1 += random.randrange(5) | ||
+ | snigelbana2 += random.randrange(5) | ||
+ | return snigelbana1, snigelbana2 | ||
- | [BILD] | + | DISTANS = 30 |
+ | visaInformation() | ||
+ | dinSnigelsNamn = lasNamn() | ||
+ | snigelbana1, snigelbana2 = tavling() | ||
+ | </pre> | ||
- | <p>Observera att om man försöker flytta en fil till en plats där det redan finns en fil med samma namn så kommer kommandot mv skriva över filen som finns i destinationen. Man får bekräfta eller makulera kommandot genom att svara på en fråga när det är DOS men i Unix/Mac OS X kommer att filen skrivas över utan några varningar. T ex om man kör följande kommando:</p> | + | När du fått det här att fungera så ska vi ta en ny titt på funktionen |
+ | tavling. Ser du att vi gör precis samma sak med variablerna snigelbana1 | ||
+ | och snigelbana2? Först sätts variablerna till noll, sedan jämför vi | ||
+ | bägge med DISTANS i while-slingans villkor, och inuti while-slingan | ||
+ | ökas bägge med ett slumpat värde. Sist returneras bägge värdena. | ||
+ | Det här kallas för <em>kodupprepning</em> och det ska man försöka | ||
+ | undvika, av följande skäl: | ||
+ | * Det blir mer kod att skriva, vilket ger fler felkällor | ||
+ | * Programmet blir svårare att underhålla - när man ska införa ändringar måste man ändra för bägge variablerna | ||
+ | * Det ser fult ut | ||
- | <div class="codebox-divided" style="width: 600px;"><div class="codecolumn1" style="_width: 180px;"><div class="title">Unix/Mac OS X</div><pre>>mv test/prog.py test/Katalog1/<br>></pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs>move test\>Prog.py test\Katalog1\ | ||
- | Overwrite C:\sommarkurs\test\Katalog1\Prog.py? (Yes/No/All): Yes | ||
- | C:\sommarkurs></pre></div></div> | ||
- | [BILD] | + | ==Skicka indata till en funktion (parametrar)== |
- | <p>Då kommer filen <span class="code">test/Katalog1/prog.py</span> ersättas med filen <span class="code">test/prog.py</span></p> | + | I det här avsnittet ska vi sona vårt stilbrott genom att skriva |
+ | en generell funktion för utskrift av en snigels bana. Funktionen ska gå | ||
+ | att använda för vilken snigel som helst. | ||
- | <p>Man kan också använda kommandot <span class="code">mv</span> för att byta namn på en fil. Anta att man vill byta namn på filen <span class="code">prog.py</span> som finns under katalogen <span class="code">test/Katalog1</span> till <span class="code">prog3.py</span>, följande kommando gör detta om aktuella katalogen är <span class="code">sommarkurs</span>:</p> | + | Utskriften av en snigelbana kan se ut så här: |
+ | <code> | ||
+ | Ebba: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @ | ||
+ | </code> | ||
+ | Vad vill vi kunna variera från en snigel till en annan? Två saker - snigelns | ||
+ | namn och banans längd. Säg att vår funktion heter <code>ritaBanan</code>. | ||
+ | Här följer tre olika exempel på hur en sån funktion skulle kunna anropas: | ||
+ | <pre> | ||
+ | ritaBanan("Ebba", 31) | ||
+ | ritaBanan("Racersnigeln", 28) | ||
+ | ritaBanan(dinSnigelsNamn, snigelBana1) | ||
+ | </pre> | ||
- | <div class="codebox-divided" style="width: 700px;"><div class="codecolumn1"><div class="title">Unix/Mac OS X</div><pre>>mv test/Katalog1/prog.py test/Katalog1/prog3.py | + | De två värden eller variabler som står innanför parenteserna i anropet |
- | > </pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs>move test/Katalog1/prog.py test/Katalog1/prog3.py | + | kallas för <em>parametrar</em>. När vi definierar funktionen måste vi |
- | C:\sommarkurs></pre></div></div> | + | namnge alla parametrarna. Vi kallar dom för <code>snigelnamn</code> |
+ | respektive <code>langd</code>. Så här ser funktionen ut. | ||
+ | <pre> | ||
+ | def ritaBanan(snigelnamn,langd): | ||
+ | print snigelnamn.rjust(12) + ":", | ||
+ | for i in range(1,langd): | ||
+ | print "-", # slemspåret | ||
+ | print "@" # snigeln | ||
+ | </pre> | ||
- | [BILD] | + | Du har nog inte sett rjust förut? Det är en strängmetod som |
+ | högerjusterar strängen i givet antal positioner. Namnen kommer | ||
+ | då att sluta i samma position (12), så att våra snigelbanor kan | ||
+ | starta i samma läge. | ||
- | <span style="color:red">[FRÅGOR]</span> | + | De två anropen av funktionen ritaBanan kommer att se ut så här: |
+ | <pre> | ||
+ | ritaBanan(dinSnigelsNamn,snigelbana1) | ||
+ | ritaBanan("Racersnigeln",snigelbana2) | ||
+ | </pre> | ||
- | ==Skapa kataloger== | + | Vi lägger till den sista funktionen också, den som ska skriva ut |
+ | vem som vann. Som indata till funktionen vill vi skicka in längden | ||
+ | på sniglarnas banor samt sniglarnas namn, alltså totalt fyra olika | ||
+ | parametrar. så här blir det: | ||
- | <p>Om man vill skapa nya kataloger kan man använda sig av kommandot <span class="code">mkdir</span> (som står för make directory, skapa katalog).</p> | + | <pre> |
+ | def utseVinnare(langd1, langd2, namn1, namn2="Racersnigeln"): | ||
+ | print langd1, langd2 | ||
+ | print "\n" | ||
+ | if langd1 >= DISTANS and langd2 >= DISTANS: | ||
+ | print "Det blev oavgjort." | ||
+ | else: | ||
+ | if langd1 >= DISTANS: | ||
+ | print "Det här loppet tog en oväntad vändning," , namn1, "vann!" | ||
+ | else: | ||
+ | print namn2, "vann, som vanligt." | ||
+ | </pre> | ||
- | <p>Anta att aktuella katalogen är sommarkurs och man vill skapa en katalog med namnet <span class="code">lab1</span> i katalogen programkod, följande kommando gör detta:</p> | + | Den sista parametern har fått ett skönsvärde, alltså ett värde som |
+ | används om vi inte skickar in något på den platsen. Då kan man om man | ||
+ | vill utelämna den parametern i anropet, på det här viset: | ||
+ | <pre> | ||
+ | utseVinnare(snigelbana1, snigelbana2, dinSnigelsNamn) | ||
+ | </pre> | ||
- | <div class="codebox-divided"><div class="codecolumn1"><div class="title">Unix/Mac OS X</div><pre>>mkdir programkod/lab1<br>></pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs> mkdir programkod\lab1<br>C:\sommarkurs></pre></div></div> | + | Nu sätter vi ihop alltihop och provkör: |
+ | <pre> | ||
+ | import random | ||
- | <p>Hade aktuella katalogen varit <span class="code">programkod</span> så hade följande kommando skapat katalogen <span class="code">lab1</span>:</p> | + | # Skriver ut informationsrutan |
+ | def visaInformation(): | ||
+ | print """ | ||
+ | --------------------------------------------------- | ||
+ | | VEM HAR DEN SNABBASTE SNIGELN? | | ||
+ | | | | ||
+ | | Här får din snigel tävla mot en vältränad | | ||
+ | | racersnigel. Skriv in namnet på din snigel | | ||
+ | | så sätter tävlingen igång! | | ||
+ | ---------------------------------------------------""" | ||
- | <div class="codebox-divided"><div class="codecolumn1"><div class="title">Unix/Mac OS X</div><pre>>mkdir lab1 | + | # Läser in namnet på användarens snigel |
- | ></pre></div><div class="codecolumn2"><div class="title">DOS</div><pre>C:\sommarkurs\programkod>mkdir lab1 | + | def lasNamn(): |
- | C:\sommarkurs\programkod></pre></div></div> | + | namn = raw_input("Vad heter din snigel? ") |
+ | return namn | ||
- | <p>så här ser filstrukturen ut efter skapandet av katalogen lab1:</p> | + | # Simulerar en tävling mellan två sniglar |
+ | def tavling(): | ||
+ | snigelbana1 = 0 | ||
+ | snigelbana2 = 0 | ||
+ | print "Klara...färdiga...gå! \n" | ||
+ | while snigelbana1 < DISTANS and snigelbana2 < DISTANS: | ||
+ | snigelbana1 += random.randrange(5) | ||
+ | snigelbana2 += random.randrange(5) | ||
+ | return snigelbana1, snigelbana2 | ||
- | [BILD] | + | # Ritar en snigelbana |
+ | def ritaBanan(snigelnamn,langd): | ||
+ | print snigelnamn.rjust(12) + ":", | ||
+ | for i in range(1,langd): | ||
+ | print "-", #slemspåret | ||
+ | print "@" # snigeln | ||
- | ==Slutprov 4== | + | # Skriver ut vinnaren |
+ | def utseVinnare(langd1, langd2, namn1, namn2="Racersnigeln"): | ||
+ | print langd1, langd2 | ||
+ | print "\n" | ||
+ | if langd1 >= DISTANS and langd2 >= DISTANS: | ||
+ | print "Det blev oavgjort." | ||
+ | else: | ||
+ | if langd1 >= DISTANS: | ||
+ | print "Det här loppet tog en oväntad vändning," , namn1, "vann!" | ||
+ | else: | ||
+ | print namn2, "vann, som vanligt." | ||
+ | |||
+ | |||
+ | DISTANS = 30 | ||
+ | visaInformation() | ||
+ | dinSnigelsNamn = lasNamn() | ||
+ | snigelbana1, snigelbana2 = tavling() | ||
+ | ritaBanan(dinSnigelsNamn,snigelbana1) | ||
+ | ritaBanan("Racersnigeln",snigelbana2) | ||
+ | utseVinnare(snigelbana1, snigelbana2, dinSnigelsNamn) | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | ==Rekursion== | ||
+ | [[Bild:Droste.jpg|Bild på kakaopaket|thumb]] | ||
+ | |||
+ | Nu börjar vi om med ett helt nytt problem. Vi vill skriva ett | ||
+ | program som räknar ut summan av dom första n heltalen, t ex | ||
+ | 1+2+3+4=10. Det här är ett så enkelt problem, så det räcker | ||
+ | med en enda funktion. Hur ska vi beräkna summan då? Vi | ||
+ | förenklar problemet lite: Säg att vi ska räkna ut summan av de | ||
+ | fem första heltalen. Då kan vi först räkna ut summan av de fyra | ||
+ | första heltalen och sen lägga till fem. Men summan av de | ||
+ | fyra första heltalen är ju lätt att räkna ut - det är ju | ||
+ | summan av de tre första heltalen plus fyra. Och så vidare... | ||
+ | |||
+ | Vi skriver summaberäkningen som en funktion, och låter den | ||
+ | räkna enligt mönstret summa(n) = summa(n-1) + n. | ||
+ | Funktionen har n som parameter, och programmet ser ut så här: | ||
+ | |||
+ | <pre> | ||
+ | def summa(n): | ||
+ | return summa(n-1) + n | ||
+ | |||
+ | print "Välkommen till summaberäkningsprogrammet!" | ||
+ | print "Här beräknas summan 1+2+3+...n" | ||
+ | n = input("Vilket tal ska vara det sista i summan? ") | ||
+ | print "Summan = ", summa(n) | ||
+ | </pre> | ||
+ | |||
+ | Provkörde du? Isåfall kan det vara bra att komma ihåg att man kan avbryta | ||
+ | ett program med Ctrl-C. Det som händer här är att funktionen summa | ||
+ | anropar sig själv i all oändlighet. Vad beror det på? | ||
+ | |||
+ | Jo, vi har glömt att tala om när beräkningen ska avbrytas. Anropar | ||
+ | vi med n=3, så kommer funktionen att försöka räkna ut | ||
+ | summa(2), summa(1), summa(0), summa(-1) och så vidare. | ||
+ | |||
+ | Vi vill ju att det minsta talet i summan ska vara 1, och det måste | ||
+ | vi ange i funktionen. Nytt försök: | ||
+ | |||
+ | <pre> | ||
+ | def summa(n): | ||
+ | if n > 1: | ||
+ | return summa(n-1) + n | ||
+ | else: | ||
+ | return 1 | ||
+ | |||
+ | print "Välkommen till summaberäkningsprogrammet!" | ||
+ | n = input("Vilket tal ska vara det sista i summan? ") | ||
+ | print "Summan = ", summa(n) | ||
+ | </pre> | ||
+ | |||
+ | Det här fungerade väl bra? Att lösa ett problem genom att låta en | ||
+ | funktion anropa sig själv kallas rekursion. Det man behöver är: | ||
+ | * ''Rekursiv tanke:'' som reducerar problemet till ett enklare problem med samma struktur | ||
+ | * ''Basfall:'' det måste finnas ett fall som inte leder till rekursivt anrop | ||
+ | |||
+ | |||
+ | ==Test== | ||
+ | |||
+ | Dags för test nummer 4. Testet hittar du som vanligt under rubriken Examination | ||
+ | på kursens förstasida. Även detta test rättas automatiskt och du har möjlighet | ||
+ | att göra om testet flera gånger om du inte lyckas på första försöket. | ||
+ | |||
+ | |||
+ | ==Inlämningsuppgift 2 (Rondelet) == | ||
+ | |||
+ | ''Inlämningsuppgiften är individuell och får inte lösas i grupp.'' | ||
+ | |||
+ | I den här inlämningsuppgiften ska du skriva ett antal funktioner för att | ||
+ | göra ett program som skapar en dikt av en inläst text. | ||
+ | Programmet ska läsa in fyra meningar och sedan skriva ut texten | ||
+ | uppdelad på följande rader: | ||
+ | |||
+ | # Textens fyra första ord | ||
+ | # Resten av första meningen | ||
+ | # Textens fyra första ord igen | ||
+ | # Andra meningen | ||
+ | # Tredje meningen | ||
+ | # Fjärde meningen | ||
+ | # Textens fyra första ord en sista gång | ||
+ | |||
+ | Texten kommer då att likna en ''rondelet'' - en fransk diktform. | ||
+ | |||
+ | Här är ett exempel på en körning: | ||
+ | |||
+ | <pre> | ||
+ | DIKTAUTOMATEN | ||
+ | |||
+ | Skriv in fyra meningar och få ut en rondelet! | ||
+ | |||
+ | Skriv mening nr 1: Det fanns ingen fil när jag handlade på Konsum. | ||
+ | Skriv mening nr 2: Bananerna var också slut. | ||
+ | Skriv mening nr 3: Jag köpte bröd istället. | ||
+ | Skriv mening nr 4: Nån sorts limpa med mycket fibrer. | ||
+ | |||
+ | |||
+ | DET FANNS INGEN FIL | ||
+ | |||
+ | Det fanns ingen fil | ||
+ | när jag handlade på Konsum. | ||
+ | Det fanns ingen fil | ||
+ | Bananerna var också slut. | ||
+ | Jag köpte bröd istället. | ||
+ | Nån sorts limpa med mycket fibrer. | ||
+ | Det fanns ingen fil | ||
+ | </pre> | ||
+ | |||
+ | ===Tips=== | ||
+ | |||
+ | Medan du testar programmet kan du ersätta anropet av din inläsningsfunktion | ||
+ | med tilldelningssatser. På det sättet slipper du skriva in fyra meningar | ||
+ | varje gång du testkör ditt program! | ||
+ | |||
+ | <pre># För testning: | ||
+ | sentence[0] = "Det fanns ingen fil när jag handlade på Konsum. | ||
+ | sentence[1] = "Bananerna var också slut. | ||
+ | sentence[2] = "Jag köpte bröd istället. | ||
+ | sentence[3] = "Nån sorts limpa med mycket fibrer."</pre> | ||
+ | |||
+ | Det finns en användbar metod i Python som heter <code>split()</code> med vars | ||
+ | hjälp du kan dela upp en mening så att det blir en lista av enskilda ord. | ||
+ | Här är ett exempel som visar hur man använder <code>split()</code>: | ||
+ | |||
+ | <pre>mening = "En ganska kort mening." | ||
+ | lista = mening.split() | ||
+ | # lista har nu värdet ["En", "ganska", "kort", "mening."]</pre> | ||
+ | |||
+ | För att göra om små bokstäver till stora i en sträng så kan du använda metoden <code>upper()</code>: | ||
+ | |||
+ | <pre>mening = "En ganska kort mening." | ||
+ | ny_mening = mening.upper() | ||
+ | # ny_mening har nu värdet "EN GANSKA KORT MENING."</pre> | ||
+ | |||
+ | ===Inlämning=== | ||
+ | |||
+ | Programmet ska finnas i en fil med namnet <code>Uppgift2.py</code>. Följ den här mallen: | ||
+ | |||
+ | <pre># Programmeringsteknik webbkurs KTH inlämningsuppgift 2. | ||
+ | # <Ditt namn> | ||
+ | # <Datum> | ||
+ | # <Kort beskrivning av vad programmet gör> | ||
+ | |||
+ | <Programkod></pre> | ||
+ | |||
+ | Innan du skickar in programmet så ska du se till att där inte | ||
+ | finns någon kodupprepning som kan åtgärdas med funktioner | ||
+ | och slingor. | ||
+ | |||
+ | När du har finputsat ditt program så går du till kursens förstasida. | ||
+ | Under rubriken Examination hittar du Inlämningsuppgift 2 och | ||
+ | där kan du lämna in filen Uppgift2.py. Observera att filen måste vara i textformat, | ||
+ | vilket den automatiskt blir om du skapar den med hjälp av IDLE. | ||
+ | Detta för att vi ska kunna prova och testköra ditt program. |
Nuvarande version
Innehåll |
[redigera] Vad har man funktioner till?
Stora program är besvärligare att konstruera än små. Men man kan göra det enklare för sig genom att dela upp programmet i funktioner. En funktion är som ett litet delprogram i programmet.
När man anordnar tävlingar med riktiga sniglar tar det mycket lång tid innan deltagarna kommer i mål. Vi ska istället skriva ett litet program som simulerar en tävling mellan två sniglar:
--------------------------------------------------- | VEM HAR DEN SNABBASTE SNIGELN? | | | | Här får din snigel tävla mot en vältränad | | racersnigel. Skriv in namnet på din snigel | | så sätter tävlingen igång! | --------------------------------------------------- Vad heter din snigel? Ebba Klara...färdiga...gå! Ebba: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @ Racersnigeln: - - - - - - - - - - - - - - - - - - - - - - - - - - - @ Det här loppet tog en oväntad vändning, Ebba vann!
[redigera] Dela upp ett problem i funktioner
För att programmet ska bli enklare att skriva bestämmer vi först vilka funktioner vi ska ha. Det är inte alls självklart hur programmet ska delas upp i funktioner; samma problem kan lösas på många olika sätt! Vi vill se till att varje funktion har ett enda uppdrag.
Här är ett förslag på uppdelning:
- Skriv ut informationsrutan och läs in namnet på snigeln
- Simulera tävlingen och rita upp banorna
- Skriv ut vem som vann
Ettan och tvåan består av flera olika moment. Vi gör det ännu enklare för oss genom att dela upp i ännu mindre delar:
- Skriv ut informationsrutan
- Läs in namnet på en snigel
- Simulera tävlingen
- Rita upp en snigels bana
- Skriv ut vem som vann
Vi börjar med att skriva in dessa moment som kommentarer i filen snigel.py, för att vi ska hålla reda på vad som är kvar att göra. Då får vi också en kommentar för varje funktion, så att det syns vad som händer var.
Fråga: Tänk om vi ville skriva ett program för stavningskontroll, i vilka funktioner skulle vi då dela upp det?
Svar:Till exempel följande: inläsning av ordlistan, kontroll av ett ord mot ordlistan, utskrift av rättelseförslag
[redigera] Definiera en funktion
Först kommer funktionshuvudet (första raden i funktionen). Exempel:
def visaInformation():
Funktionshuvudet består av fyra delar.
- Ordet def (talar om att en funktion ska definieras här)
- Funktionens namn, gärna ett verb som beskriver vad funktionen gör
- Ett par parenteser ()
- Ett kolon
Under funktionshuvudet kommer funktionskroppen, som helt enkelt är en följd av indenterade satser som funktionen ska utföra. Så här ser hela funktionen ut:
# Skriver ut informationsrutan def visaInformation(): print """ --------------------------------------------------- | VEM HAR DEN SNABBASTE SNIGELN? | | | | Här får din snigel tävla mot en vältränad | | racersnigel. Skriv in namnet på din snigel | | så sätter tävlingen igång! | ---------------------------------------------------"""
(För att kunna skriva en sträng som sträcker sig över flera rader så startar och avslutar vi strängen med tre stycken "-tecken. Vanliga trängar, som börjar och slutar med enkelt citattecken, kan nämligen inte innehålla radbrytningar.)
Lägg till koden ovan i din fil snigel.py och provkör.
Men vad nu - programmet skriver inte ut något alls? Gå vidare till nästa avsnitt för att få reda på varför!
[redigera] Anropa en funktion
Satserna i funktionskroppen utförs inte om inte funktionen anropas. Anropet ska stå längst ner i programmet, under funktionsdefinitionerna. Den delen av programmet kallar vi huvudprogrammet.
Anropet består bara av funktionens namn följt av ett parentespar. Om du lägger till anropet sist i ditt program ska alltihop se ut så här:
# Skriver ut informationsrutan def visaInformation(): print """ --------------------------------------------------- | VEM HAR DEN SNABBASTE SNIGELN? | | | | Här får din snigel tävla mot en vältränad | | racersnigel. Skriv in namnet på din snigel | | så sätter tävlingen igång! | ---------------------------------------------------""" # Läs in namnet på en snigel # Simulera tävlingen # Rita upp en snigels bana # Skriv ut vem som vann visaInformation()
Spara i filen snigel.py och provkör!
[redigera] Skicka utdata från en funktion (returvärden)
Då ger vi oss på nästa funktion, som ska läsa in namnet på en snigel. Namnet på snigeln är utdata från funktionen. Den som kör programmet och knappar in namnet tycker förstås att det är indata, men ur funktionens synvinkel är det nåt som ska skickas ut.
För att skicka ut ett värde ur funktionen skriver vi ordet return följt av den variabel vi vill skicka ut värdet på. Funktionen som läser in namnet kommer att se ut så här:
def lasNamn(): namn = raw_input("Vad heter din snigel? ") return namn
När vi ska anropa funktionen måste vi ta hänsyn till att den returnerar ett värde. Därför skriver vi anropet i en tilldelningssats, med en variabel till vänster som får ta emot det returnerade värdet. Variabeln behöver inte ha samma namn som variabeln i return-satsen inuti funktionen.
dinSnigelsNamn = lasNamn()
När den här satsen utförs av Python kommer följande att hända. Först anropas funktionen lasNamn, som läser in namnet och returnerar det. Sen kommer det returnerade värdet att lagras i variabel dinSnigelsNamn.
Vi lägger in den nya i filen snigel.py och provkör.
# Skriver ut informationsrutan def visaInformation(): print """ --------------------------------------------------- | VEM HAR DEN SNABBASTE SNIGELN? | | | | Här får din snigel tävla mot en vältränad | | racersnigel. Skriv in namnet på din snigel | | så sätter tävlingen igång! | ---------------------------------------------------""" # Läs in namnet på en snigel def lasNamn(): namn = raw_input("Vad heter din snigel? ") return namn # Simulera tävlingen # Rita upp en snigels bana # Skriv ut vem som vann visaInformation() dinSnigelsNamn = lasNamn()
Men hur ska vi veta om det fungerade eller inte? Jo, vi lägger in en kontrollutskrift
print "Din snigel heter alltså", dinSnigelsNamn
allra sist i programmet. Kontrollutskrifter är ett enkelt sätt att se om programmet fungerar som det ska, och är till stor nytta när man försöker ta reda på varför programmet inte gör som det ska.
Provkör igen. När programmet fungerar kan du ta bort kontrollutskriften!
Det här är viktigt, så vi tar ett exempel till, den här gången med två returvärden. Nästa funktion ska simulera tävlingen, på det här viset:
- Sniglarna startar bägge på startstrecket, dvs vid 0
- Vi skriver ut Klara...färdiga...gå
- Och startar själva tävlingen, som representeras av en while-slinga
- I varje varv i slingan låter vi sniglarna ta varsitt skutt av slumpmässig längd
- När någon av sniglarna (eller bägge) passerar mållinjen så avbryter vi
- Sist returnerar vi bägge sniglarnas slutpositioner
def tavling(): snigelbana1 = 0 snigelbana2 = 0 print "Klara...färdiga...gå! \n" while snigelbana1 < DISTANS and snigelbana2 < DISTANS: snigelbana1 += random.randrange(5) snigelbana2 += random.randrange(5) return snigelbana1, snigelbana2
I return-satsen allra sist kan vi se att två värden returneras, åtskilda med kommatecken. Givetvis måste vi då också ta emot två värden i huvudprogrammet.
Den distans (i centimeter) som sniglarna ska tillryggalägga representeras av konstanten DISTANS. En variabel som ska ha ett konstant värde under hela programmet brukar man skriva med stora bokstäver, VERSALER. Python bryr sig inte om det, men andra människor som läser programmet kan se vad man menar. Vi definierar DISTANS överst i huvudprogrammet, och frågar oss om den kommer att synas inuti funktionen?
Lägg in den nya funktionen i snigel.py, enligt nedan. Glöm inte att lägga in kontrollutskrifter av snigelbana1 och snigelbana2 innan du provkör!
import random # Skriver ut informationsrutan def visaInformation(): print """ --------------------------------------------------- | VEM HAR DEN SNABBASTE SNIGELN? | | | | Här får din snigel tävla mot en vältränad | | racersnigel. Skriv in namnet på din snigel | | så sätter tävlingen igång! | ---------------------------------------------------""" # Läser in namnet på användarens snigel def lasNamn(): namn = raw_input("Vad heter din snigel? ") return namn # Simulerar en tävling mellan två sniglar def tavling(): snigelbana1 = 0 snigelbana2 = 0 print "Klara...färdiga...gå! \n" while snigelbana1 < DISTANS and snigelbana2 < DISTANS: snigelbana1 += random.randrange(5) snigelbana2 += random.randrange(5) return snigelbana1, snigelbana2 DISTANS = 30 visaInformation() dinSnigelsNamn = lasNamn() snigelbana1, snigelbana2 = tavling()
När du fått det här att fungera så ska vi ta en ny titt på funktionen tavling. Ser du att vi gör precis samma sak med variablerna snigelbana1 och snigelbana2? Först sätts variablerna till noll, sedan jämför vi bägge med DISTANS i while-slingans villkor, och inuti while-slingan ökas bägge med ett slumpat värde. Sist returneras bägge värdena. Det här kallas för kodupprepning och det ska man försöka undvika, av följande skäl:
- Det blir mer kod att skriva, vilket ger fler felkällor
- Programmet blir svårare att underhålla - när man ska införa ändringar måste man ändra för bägge variablerna
- Det ser fult ut
[redigera] Skicka indata till en funktion (parametrar)
I det här avsnittet ska vi sona vårt stilbrott genom att skriva en generell funktion för utskrift av en snigels bana. Funktionen ska gå att använda för vilken snigel som helst.
Utskriften av en snigelbana kan se ut så här:
Ebba: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @
Vad vill vi kunna variera från en snigel till en annan? Två saker - snigelns
namn och banans längd. Säg att vår funktion heter ritaBanan
.
Här följer tre olika exempel på hur en sån funktion skulle kunna anropas:
ritaBanan("Ebba", 31) ritaBanan("Racersnigeln", 28) ritaBanan(dinSnigelsNamn, snigelBana1)
De två värden eller variabler som står innanför parenteserna i anropet
kallas för parametrar. När vi definierar funktionen måste vi
namnge alla parametrarna. Vi kallar dom för snigelnamn
respektive langd
. Så här ser funktionen ut.
def ritaBanan(snigelnamn,langd): print snigelnamn.rjust(12) + ":", for i in range(1,langd): print "-", # slemspåret print "@" # snigeln
Du har nog inte sett rjust förut? Det är en strängmetod som högerjusterar strängen i givet antal positioner. Namnen kommer då att sluta i samma position (12), så att våra snigelbanor kan starta i samma läge.
De två anropen av funktionen ritaBanan kommer att se ut så här:
ritaBanan(dinSnigelsNamn,snigelbana1) ritaBanan("Racersnigeln",snigelbana2)
Vi lägger till den sista funktionen också, den som ska skriva ut vem som vann. Som indata till funktionen vill vi skicka in längden på sniglarnas banor samt sniglarnas namn, alltså totalt fyra olika parametrar. så här blir det:
def utseVinnare(langd1, langd2, namn1, namn2="Racersnigeln"): print langd1, langd2 print "\n" if langd1 >= DISTANS and langd2 >= DISTANS: print "Det blev oavgjort." else: if langd1 >= DISTANS: print "Det här loppet tog en oväntad vändning," , namn1, "vann!" else: print namn2, "vann, som vanligt."
Den sista parametern har fått ett skönsvärde, alltså ett värde som används om vi inte skickar in något på den platsen. Då kan man om man vill utelämna den parametern i anropet, på det här viset:
utseVinnare(snigelbana1, snigelbana2, dinSnigelsNamn)
Nu sätter vi ihop alltihop och provkör:
import random # Skriver ut informationsrutan def visaInformation(): print """ --------------------------------------------------- | VEM HAR DEN SNABBASTE SNIGELN? | | | | Här får din snigel tävla mot en vältränad | | racersnigel. Skriv in namnet på din snigel | | så sätter tävlingen igång! | ---------------------------------------------------""" # Läser in namnet på användarens snigel def lasNamn(): namn = raw_input("Vad heter din snigel? ") return namn # Simulerar en tävling mellan två sniglar def tavling(): snigelbana1 = 0 snigelbana2 = 0 print "Klara...färdiga...gå! \n" while snigelbana1 < DISTANS and snigelbana2 < DISTANS: snigelbana1 += random.randrange(5) snigelbana2 += random.randrange(5) return snigelbana1, snigelbana2 # Ritar en snigelbana def ritaBanan(snigelnamn,langd): print snigelnamn.rjust(12) + ":", for i in range(1,langd): print "-", #slemspåret print "@" # snigeln # Skriver ut vinnaren def utseVinnare(langd1, langd2, namn1, namn2="Racersnigeln"): print langd1, langd2 print "\n" if langd1 >= DISTANS and langd2 >= DISTANS: print "Det blev oavgjort." else: if langd1 >= DISTANS: print "Det här loppet tog en oväntad vändning," , namn1, "vann!" else: print namn2, "vann, som vanligt." DISTANS = 30 visaInformation() dinSnigelsNamn = lasNamn() snigelbana1, snigelbana2 = tavling() ritaBanan(dinSnigelsNamn,snigelbana1) ritaBanan("Racersnigeln",snigelbana2) utseVinnare(snigelbana1, snigelbana2, dinSnigelsNamn)
[redigera] Rekursion
Nu börjar vi om med ett helt nytt problem. Vi vill skriva ett program som räknar ut summan av dom första n heltalen, t ex 1+2+3+4=10. Det här är ett så enkelt problem, så det räcker med en enda funktion. Hur ska vi beräkna summan då? Vi förenklar problemet lite: Säg att vi ska räkna ut summan av de fem första heltalen. Då kan vi först räkna ut summan av de fyra första heltalen och sen lägga till fem. Men summan av de fyra första heltalen är ju lätt att räkna ut - det är ju summan av de tre första heltalen plus fyra. Och så vidare...
Vi skriver summaberäkningen som en funktion, och låter den räkna enligt mönstret summa(n) = summa(n-1) + n. Funktionen har n som parameter, och programmet ser ut så här:
def summa(n): return summa(n-1) + n print "Välkommen till summaberäkningsprogrammet!" print "Här beräknas summan 1+2+3+...n" n = input("Vilket tal ska vara det sista i summan? ") print "Summan = ", summa(n)
Provkörde du? Isåfall kan det vara bra att komma ihåg att man kan avbryta ett program med Ctrl-C. Det som händer här är att funktionen summa anropar sig själv i all oändlighet. Vad beror det på?
Jo, vi har glömt att tala om när beräkningen ska avbrytas. Anropar vi med n=3, så kommer funktionen att försöka räkna ut summa(2), summa(1), summa(0), summa(-1) och så vidare.
Vi vill ju att det minsta talet i summan ska vara 1, och det måste vi ange i funktionen. Nytt försök:
def summa(n): if n > 1: return summa(n-1) + n else: return 1 print "Välkommen till summaberäkningsprogrammet!" n = input("Vilket tal ska vara det sista i summan? ") print "Summan = ", summa(n)
Det här fungerade väl bra? Att lösa ett problem genom att låta en funktion anropa sig själv kallas rekursion. Det man behöver är:
- Rekursiv tanke: som reducerar problemet till ett enklare problem med samma struktur
- Basfall: det måste finnas ett fall som inte leder till rekursivt anrop
[redigera] Test
Dags för test nummer 4. Testet hittar du som vanligt under rubriken Examination på kursens förstasida. Även detta test rättas automatiskt och du har möjlighet att göra om testet flera gånger om du inte lyckas på första försöket.
[redigera] Inlämningsuppgift 2 (Rondelet)
Inlämningsuppgiften är individuell och får inte lösas i grupp.
I den här inlämningsuppgiften ska du skriva ett antal funktioner för att göra ett program som skapar en dikt av en inläst text. Programmet ska läsa in fyra meningar och sedan skriva ut texten uppdelad på följande rader:
- Textens fyra första ord
- Resten av första meningen
- Textens fyra första ord igen
- Andra meningen
- Tredje meningen
- Fjärde meningen
- Textens fyra första ord en sista gång
Texten kommer då att likna en rondelet - en fransk diktform.
Här är ett exempel på en körning:
DIKTAUTOMATEN Skriv in fyra meningar och få ut en rondelet! Skriv mening nr 1: Det fanns ingen fil när jag handlade på Konsum. Skriv mening nr 2: Bananerna var också slut. Skriv mening nr 3: Jag köpte bröd istället. Skriv mening nr 4: Nån sorts limpa med mycket fibrer. DET FANNS INGEN FIL Det fanns ingen fil när jag handlade på Konsum. Det fanns ingen fil Bananerna var också slut. Jag köpte bröd istället. Nån sorts limpa med mycket fibrer. Det fanns ingen fil
[redigera] Tips
Medan du testar programmet kan du ersätta anropet av din inläsningsfunktion med tilldelningssatser. På det sättet slipper du skriva in fyra meningar varje gång du testkör ditt program!
# För testning: sentence[0] = "Det fanns ingen fil när jag handlade på Konsum. sentence[1] = "Bananerna var också slut. sentence[2] = "Jag köpte bröd istället. sentence[3] = "Nån sorts limpa med mycket fibrer."
Det finns en användbar metod i Python som heter split()
med vars
hjälp du kan dela upp en mening så att det blir en lista av enskilda ord.
Här är ett exempel som visar hur man använder split()
:
mening = "En ganska kort mening." lista = mening.split() # lista har nu värdet ["En", "ganska", "kort", "mening."]
För att göra om små bokstäver till stora i en sträng så kan du använda metoden upper()
:
mening = "En ganska kort mening." ny_mening = mening.upper() # ny_mening har nu värdet "EN GANSKA KORT MENING."
[redigera] Inlämning
Programmet ska finnas i en fil med namnet Uppgift2.py
. Följ den här mallen:
# Programmeringsteknik webbkurs KTH inlämningsuppgift 2. # <Ditt namn> # <Datum> # <Kort beskrivning av vad programmet gör> <Programkod>
Innan du skickar in programmet så ska du se till att där inte finns någon kodupprepning som kan åtgärdas med funktioner och slingor.
När du har finputsat ditt program så går du till kursens förstasida. Under rubriken Examination hittar du Inlämningsuppgift 2 och där kan du lämna in filen Uppgift2.py. Observera att filen måste vara i textformat, vilket den automatiskt blir om du skapar den med hjälp av IDLE. Detta för att vi ska kunna prova och testköra ditt program.