Göm menyn

3. Bearbetning av listor

3.1 Inledning

I den första laborationen övade du på iteration och rekursion över tal och raka listor. I den här laborationen tränas iterativa och rekursiva sätt att bearbeta mer komplicerade strukturer: listor som innehåller listor, tupler (immuterbara listor) och dictionaries. Vi kommer också att på ett tydligare sätt jämföra iteration och rekursion.

Föreläsning 4 och 7 förbereder för denna laboration. Följande avsnitt i kursboken är särskilt intressanta: 2.6 (iteration över ranges), 5.1-6 (strängar), 11.1-3 (listor), 11.6 (dictionaries).

Den här labbomgången innehåller fler övningar än du kommer hinna med att göra. Du får själv försöka prioritera bort några av dem, så att du hinner med uppgifterna som ska redovisas.

OBS! Från och med den här laborationen ska ni laborera i par. Ni väljer grupper själva och det går till så här:

  • Logga in i Webreg och skriv upp dig i någon av grupperna. Detta måste du göra senast tisdag 23 september, men gärna mycket tidgare än så.
  • Skriv upp dig på en av grupperna i din klass, det spelar ingen roll vilken. Det behöver inte vara samma grupp som under de första laborationerna.
  • Om du vet vem du vill jobba ihop med, se till att ni skriver upp er på samma gång i samma par.
  • Om du inte vet vem du vill jobba med, skriv upp dig ensam. Om det redan finns någon som skrivit upp sig ensam måste du skriva upp dig tillsammans med den.

Om något blir fel i anmälan, kontakta din labbassistent. Börja inte med laborationen förrän du har hittat någon att jobba med. Det kan vara en god idé att försöka hitta någon som har ungefär samma förkunskaper och ambitionsnivå som dig.

2014-09-09: Uppdaterad med datum och länkar för hösten 2014.

3.2 Nya datatyper

I detta avsnitt introduceras typerna tupel och dictionary, och ett par små men viktiga detaljer om tillåtna värden repeteras.

Övning 301 Tupler fungerar i princip på samma sätt som listor, men med två noterbara undantag. Den första skillnaden är att tupler står inom vanliga parenteser istället för hakparenteser. Den andra skillnaden kommer du att upptäcka i den här övningen. Tupler kan, precis som listor, innehålla vilka typer av data som helst. Här har vi ett par exempel på representationer av (x, y)-koordinater.

>>> pos_one = (5, 0) >>> pos_two = ('fem', 'noll') >>> pos_three = ([-5], [0])

Skriv kommandon som tar fram x-koordinaten respektive y-koordinaten ur var och en av de tre tuplerna ovan. Hur ändrar man koordinaten för pos_one till (5, 5), om det överhuvudtaget är möjligt? Kan det ändras "på plats", utan att använda uttryck som pos_one = ... på liknande sätt som man ändrar enstaka element i en lista? Kan du göra motsvarande med de andra tuplerna?

Övning 302 I en dictionary parar man ihop en nyckel med data. Därefter kan man ta fram data genom att använda nyckeln. Man kan tänka på dem som en slags avancerade listor där man, istället för att använda ett tal som index, använder i princip vilken typ av data som helst. Prova att skriva in följande:

>>> numbers = {"one": 1, "two": 2, "seven": 6, "eight": 9} >>> numbers["one"] 1

För att bekanta dig med hur man använder dictionaries, skriv nu kommandon som gör följande:

  • lägger till nyckeln "three" kopplat till lämplig siffra i svenska_siffror.
  • ändrar så att numbers["eight"] ger siffran 8

Övning 303 Här nedanför ser du två sätt att försöka skapa dictionaries. Ett av dem fungerar, det andra gör det inte. Försök förutse vilket som fungerar. Pröva sedan att skriva in det vid Python-prompten och se om du hade rätt.

>>> coodinates_one = { (1, 0, 0): "x-axis", (0, 1, 0): "y-axis" } >>> coordinates_two = { [1, 0, 0]: "x-axis", [0, 1, 0]: "y-axis" }

Varför funkar bara det ena? Försök också hitta en lista eller tupel (det av exemplen som fungerade här ovanför) som inte fungerar att ha som nyckel.

3.3 Mer om iteration

Iteration handlar om att utföra en följd av instruktioner flera gånger. När det handlar om att utföra samma sak med varje element i t.ex. en lista brukar vi säga att vi itererar över listan. Vi kan köra det genom att använda explicit index så här:

for i in range(len(my_list)): print(my_list[i])

Här räknar vi ut listans längd och låter variabeln i anta alla värden i intervallet 0 till listans längd minus ett. På det sättet behandlar vi alla element. Ett sätt som många gånger kan vara lättare är att göra så här:

for e in my_list: print(e)

Här använder vi inget index. Det tar for-loopen själv hand om. Den erbjuder oss elementen i listan, ett i taget, i variabeln e. Detta sätt funkar inte bara på listor och strängar, utan också på i princip alla sammansatta datatyper som kan indexeras. Till exempel funkar det för tupler och dictionaries som vi ju redan har övat på lite grann. När det gäller dictionaries itererar vi främst över indexmängden, d.v.s. alla nycklarna.

I de följande övningarna tänker vi oss främst iterativa lösningar, d.v.s. du behöver inte konstruera rekursiva varianter.

Övning 304 Konstruera en funktion show_wordlist som går genom en dictionary och skriver ut vad de olika nycklarna svarar mot.

>>> show_wordlist(numbers) -- one - 1 eight - 8 two - 2 --

Den exakta ordningen mellan elementen spelar ingen roll. Den kan variera. Hur anpassar du for-loopen så att du kan gå igenom en dictionary?

Övning 305 Konstruera en funktion all_pairs som utifrån en lista ger en lista med alla par, d.v.s. alla möjliga sätt att kombinera två element ur originallistan. Paren representeras som tupler och vi ser dem som ordnade. Det innebär att om x och y är två olika element, så är (x, y) och (y, x) två olika möjliga par.

>>> all_pairs([1, 2, 3]) [(1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3)]

Om vi skickar in en lista av längden n ska vi givetvis få en lista med n^2 antal par i som resultat.

Övning 306 Konstruera en funktion unordered_pairs som utifrån en lista ger en lista med alla oordnade par. Utdata ska vara precis som i föregående övning, tupler i en lista. Skillnaden är att här ser vi paren som oordnade. Det innebär t.ex. att om paret (1, 2) finns med i resultatet, så får inte (2, 1) vara det. Par som (1, 1) tillåts dock, så länge som det bara finns en kopia av dem.

>>> unordered_pairs([1, 2, 3]) [(1,1), (1,2), (1,3), (2,2), (2,3), (3,3)]

Om vi skickar in en lista av längden n ska vi här få en lista med n(n+1)/2 antal par i som resultat.

Övning 307 Konstruera en funktion distribute som tar ett element och en lista av listor, och lägger in elementet i varje dellista. Listan man skickar in ska inte ändras, utan funktionen ska generera en helt ny lista.

>>> old = [['tesc'], ['fo'], [1]] >>> distribute("o", old) [['tesc', 'o'], ['fo', 'o'], [1, 'o']] >>> old # har listan ändrats? [['tesc' ], ['fo'], [1]]

Övning 308 Konstruera en funktion extend_each som tar ett element och en lista av listor, och lägger in elementet i varje dellista. Skillnaden mot funktionen ovan är att denna funktion faktiskt ska ändra på listan som skickades in.

>>> extend_each("o", old) >>> old # har listan ändrats? [['tesc', 'o'], ['fo', 'o'], [1, 'o']]

Övning 309 Konstruera en funktion push_strings som tar en lista och knuffar alla strängar ett steg fram. Den första strängen hamnar alltså på den plats den andra strängen hade förut, och så vidare. På den första strängens plats ska det stå 0. Den sista strängen försvinner. Listan ska inte modifieras, utan en ny lista ska returneras.

>>> eggs = [1, 't', 3, 4, 'e', 5, 's'] >>> eggs = push_strings(eggs) >>> eggs [1, 0, 3, 4, 't', 5, 'e'] >>> push_strings(eggs) [1, 0, 3, 4, 0, 5, 't']

Lös problemet på två olika sätt, först i en version som itererar över listans element (for element in list), sedan i en version som itererar över indexmängden (for i in range(len(list))). Hur skiljer sig lösningarna åt?

Uppgift 3A - Dolda budskap

Din uppgift är att skriva två funktioner split_rec och split_it som i ett pass delar upp en given sträng i två delar. En ska bestå av alla gemener (små bokstäver), understreck ("_") och punkter. En består av alla versaler (stora bokstäver), mellanslag (" ") och pipes ("|"). Alla andra tecken ska kastas bort. Tecknen ska komma i samma ordning som de gör i indatasträngen.

Den ena funktionen ska vara rent funktionell och rekursiv. Den andra ska vara iterativ.

>>> first_message,second_message=split_rec("hTEeSj_CO") >>> print(first_message, second_message) hej_ TESCO >>> print(split_rec("'lMiED)teD5E,_hLAe;Nm,0@Dli&Eg ,#4aI?rN@T§&e7#4E #<(S0A?<)NT8<0'")) ('lite_hemligare', 'MEDDELANDE INTE SANT')

I filen budskap.py finns det ett par kodade strängar som du kan testa ditt program på.

OBS! Funktionen får bara läsa genom strängen en gång. Du får alltså inte ta ut den ena gruppen bokstäver först, och den andra sedan.

Tips Det finns färdiga uppsättningar stora/små bokstäver att tillgå (i string). Det kan vara värt att leta upp (för att få vana att leta i Pythondokumentationen, eller på nätet). Versaler heter "upper case letters", gemener "lower case".

OBS! Från och med den här laborationen sker redovisning genom versionshanteringsverktyget Subversion. Läs mer om hur det fungerar rent praktiskt i vår introduktion.

När du har laddat upp samtliga uppgifter för laborationsomgång 3, kontakta din assistent (muntligen eller via e-post) och meddela att ni är klara. Assistenten kommer sedan att checka ut er kod och kolla igenom den. Återkoppling får ni sedan genom incheckade kommentarer.

3.4 Dubbelrekursion

I laboration 1 stötte du på enkelrekursion, d.v.s. rekursion där vi bara bearbetade den översta nivån i listan. Nu använder vi liknande mönster för att hantera strukturer eller beräkningar som i någon mening förgrenar sig ("listor i listor"). Du kan läsa mer om detta i materialet från föreläsning 6.

Övning 310 Skriv en funktion sum_all_numbers som lägger samman alla tal i en lista, oavsett vilken nivå de ligger på. Listan kan i sin tur innehålla listor, och alla tal ska läggas samman.

>>> sum_all_numbers(["a", "b", ["c", 3, 4, [5, "d"], 10], 9]) 31

Övning 311 Skriv en funktion exists som avgör om en viss bokstav finns på någon nivå i en lista. Det är ett predikat, så returvärdet ska vara True eller False.

>>> exists("c", ["a", ["h", "e", "j"], ["t", "e", "s", "c", "o"]]) True

Övning 312 Skriv en funktion without_vowels som utifrån en lista ger dig en ny likadan struktur, men utan alla vokaler. OBS! Här kan vi ha listor-i-listor (till skillnad från i laboration 1).

>>> test = ["a", ["h", "e", "j"], ["t", "e", "s", "c", "o"]] >>> without_vowels(test) [['h', 'j'], ['t', 's', 'c']]

3.5 Binärträd - rekursivt definierade strukturer

En klassisk datastruktur är det binära trädet, ett träd där varje förgrening maximalt utmynnar i två grenar (subträd). En speciell sorts binärträd är binära sökträd som kan användas för att lagra stora mängder information som man snabbt behöver hitta. I ett korrekt binärt sökträd är alla värden i vänstra grenen mindre än värdena i högra grenen. Exempel:

5 / \ 3 7 / / \ 1 6 8

Informationen som lagras i trädet kallas nycklar, i ovanstående exempel enbart heltal. Varje punkt i trädet med en nyckel kallas en nod, och noden "längst upp" i trädet kallas rotnoden. I detta träd har roten värdet 5. Trädet har också tre noder som är löv (1, 6 och 8), d.v.s. punkter i trädet som inte förgrenar sig vidare. De noder som inte är löv kallar vi för inre noder. Det finns också några subträd, d.v.s. träd som börjar en bit in i strukturen. Du kommer att läsa mer om träd inom den diskreta matematiken, men detta räcker för stunden. Här är samma exempel nedbrutet i sina beståndsdelar:

Hela trädet Subträd (i) Subträd (ii) Löv (iii) Löv (iv) Löv (v) 5 / \ 3 7 3 7 / / \ / / \ 1 6 8 1 6 8 1 6 8

Vi ser att rotnoden har två barn, 3 och 7. De är i sin tur rotnoder för var sitt subträd (i) och (ii). Det vänstra subträdet har ett barn (1), det högra har två (6, 8). Dessa barn är i sin tur träd - löv med enstaka värden. Om vi betraktar det som att 3 har ett tomt träd som högerträd, ser vi att ett träd definitionsmässigt kan vara tre olika saker:

  1. Inget alls (det tomma trädet)
  2. En enda nyckel (trädet är enbart ett löv)
  3. En nyckel samt två subträd (vänster och höger), där åtminstone något av subträden har ett innehåll

Den här datastrukturen är rekursivt definierad eftersom vi i den tredje punkten hänvisar tillbaka till hela definitionen för vad ett träd är. Binära sökträd borde därför lämpa sig utmärkt att bearbeta rekursivt, gärna med utgångspunkt i ovanstående definition.

Representation

Än så länge har vi bara beskrivit hur binära sökträd ser ut och ritat upp dem lite informellt. Om vi vill kunna få in dem i Python och bearbeta dem måste vi komma på ett sätt att representera dem med hjälp av de inbyggda datastrukturerna i Python.

I de här övningarna väljer vi representationen [<vänster subträd>, <nyckel>, <höger subträd>] för en inre nod, [] för det tomma trädet medan nycklarna är vanliga heltal. Subträdet (i) med sin tomma högergren, från exemplet ovan, ser alltså ut så här:

[1, 3, []]

Subträd (ii) ser ut så här:

[6, 7, 8]

Hela trädet blir då:

[[1, 3, []], 5, [6, 7, 8]]

Jämför hur vi ritade upp detta binära sökträd i exemplet ovan med listrepresentationen för Python. Det är antaligen mycket lättare för dig att visualisera trädet om du ritar upp det så som vi gjorde först i det här avsnittet. Tittar man bara på listrepresentationen är det dock rätt krånligt att se hur trädet hänger ihop. När du gör övningarna och uppgiften nedan kan det vara bra att rita upp dina exempelträd, så som vi har gjort ovan, innan du översätter dem till listformat.

Övningar

För att kunna jobba med binära sökträd finns i filen tree.py ett antal hjälpfunktioner:

  • is_empty_tree kontrollerar om trädet är tomt
  • is_leaf kontrollerar om trädet är ett löv
  • create_tree tar två subträd och ett nyckelvärde och skapar ett träd av dessa
  • left_subtree returnerar det vänstra subträdet
  • right_subtree returnerar det högra subträdet

Alla dessa funktioner är mycket korta och innehåller bara enkla manipuleringar av listor. Tanken med att använda dem är att koden blir betydligt mer lättläst. Istället för att skriva my_tree[0] kan vi skriva left_subtree(my_tree) och hjälper på så sätt den som ska läsa koden. Dessutom kommer vi att kunna ändra representation på träden i efterhand, eller använda program skrivna av folk som använder en annan representation (men samma selectors) direkt, utan att behöva fundera på om trädet i Python skrivs som listor, tupler eller något annat.

Övning 313 Skriv en funktion key, som tar ett träd (inre nod eller löv) och ger tillbaka nyckeln.

>>> key([[1, 3, []], 5, 6]) 5

Övning 314 Skriv en funktion is_treelike, som tar något indata, och kontrollerar att det verkar vara ett träd (kontroll av subträd behöver inte ske).

>>> is_treelike([[1, 3, []], 5, 6]) True >>> is_treelike([1, 2, ['fel','data']]) True >>> is_treelike([]) True >>> is_treelike(42) False

Övning 315 Skriv en funktion is_proper_tree, som tar någon slags struktur, och undersöker om det är ett träd. Här måste man såklart kontrollera att inte bara översta nivån verkar vara ett träd (lista, 3 element,...). Skapa gärna hjälpfunktioner med väl valda namn, och använd de givna funktionerna så långt det går.

>>> is_tree([]) True >>> is_proper_tree([[1, 3, []], 5, 6]) True >>> is_proper_tree([[[]], 5, 6]) False

Övning 316 I ett binärt sökträd är alla nycklar i vänster subträd mindre än eller lika med nyckeln i rotnoden, och alla till höger är större. Skriv en funktion exists_in_tree, som undersöker om ett visst värde på ett löv finns i trädet. Du behöver inte alltid gå igenom alla värden i trädet (så som vi gjorde i uppgift 301).

>>> exists_in_tree(6, [[1, 3,[]], 5, [6, 7, 8] ]) # exempelträdet ovan True >>> exists_in_tree(666, [[1, 3,[]], 5, [6, 7, 8] ]) False >>> exists_in_tree(7, [[1, 3,[]], 5, [6, 7, 8] ]) True

Övning 317 Skriv en funktion insert som - i ett färdigt binärt sökträd - lägger till ett värde på rätt plats. Det kan hända att värden som tidigare var löv blir inre noder, och du kan anta att värdet inte redan finns i trädet. (Det är annars lätt att lägga till en kontroll innan funktionen körs.)

>>> our_tree = insert(5, []) # börja med ett tomt träd 5 >>> our_tree = insert(10, our_tree) # 10 är större än 5, så det hamnar i höger subträd [[], 5, 10] >>> our_tree = insert(7, our_tree) # 7 är större än 5, mindre än 10 [[], 5, [7, 10, []]] >>> our_tree = insert(1, our_tree) [1, 5, [7, 10, []]]

Övning 318 Skriv en funktion print_tree som skriver ut löven storleksordning.

>>> print_tree(our_tree) # trädbygge från exempelkoden ovan 1 7

Uppgift 3B - Logikvärde

Matematiska uttryck består av variabler och konstanter, som kopplas ihop med hjälp av operatorer. Ibland används också parenteser för att markera vilka delar som hör ihop. Som exempel kan vi ta 100x + (5 - y) + 66. Om vi har en variabeltabell där vi kan läsa att x = 10, y = 5 kan vi beräkna värdet av uttrycket ovan till 100*10 + (5 - 5) + 66 = 1066.

I denna uppgift ska vi istället behandla logiska uttryck inom satslogiken. Dessa liknar matematiska uttryck, men istället för tal används endast två värden: sant och falskt. I logiska uttryck kallas variablerna för propositionssymboler och operatorerna för konnektiv. Där vi i matematiken hade en variabeltabell, har vi nu en tolkning. Har vi uttrycket door_open AND cat_gone, är den sann om vi har en tolkningen där både öppen_dörr=sant och cat_gone=sant.

Din uppgift är att skriva en funktion interpret som tar två argument: dels ett logiskt uttryck och en tolkning (en tabell där varje propositionssymbol tilldelas värdet sant eller värdet falskt). Tolkningen görs lämpligen i form av ett dictionary, som vi har gjort i exemplet nedan.

Ett logiskt uttryck kan i denna uppgift vara ett av följande (och inget annat):

  • en av de logiska konstanterna "true" eller "false" (som alltså ska vara dessa strängar, inte True och False)
  • en propositionssymbol inom citattecken, t ex "p", "q", "cat_gone"
  • ett uttryck på formen ["NOT", <logiskt uttryck>]
  • ett uttryck på formen [<logiskt uttryck 1>, "OR", <logiskt uttryck 2>]
  • ett uttryck på formen [<logiskt uttryck 1>, "AND", <logiskt uttryck 2>]

Ett exempel på ett giltigt uttryck här är alltså ["cat_asleep", "OR", ["NOT", "cat_gone"]]. För att bevisa att det faktiskt är giltigt kan vi först konstatera att det matchar den fjärde punkten ovan, d.v.s. det är på formen [<något 1>, "OR", <något 2>]. I nästa steg måste vi sedan bevisa att både <något 1> och <något 2> är giltiga logiska uttryck.

  1. Uttrycket <något 1> är en vanlig stäng "cat_asleep", vilket här en propositionssymbol. Den biten är alltså okej. Om det sedan är sant eller falskt beror på vilken tolkning vi gör, d.v.s. vilket värde vi ger påståendet.
  2. Uttrycket <något 2> är på formen ["NOT", <något>]. Detta stämmer överens med mönstret i punkt 3 ovan, så vi kontrollerar om <något> är ett korrekt logiskt uttryck, vilket det är (propositionssymbolen "cat_gone").

Ett exempel på ett ogiltigt uttryck i detta logikspråk är ["cat_asleep", "OR", "door_open", "OR", "cat_gone"].

Din uppgift är att skriva en funktion interpret som tar två argument, ett giltigt uttryck och en tolkning, och ger sanningsvärdet för om hela uttrycket är sant i just denna tolkning.

>>> interpret(["door_open", "AND", "cat_gone"], {"door_open" : "false", "cat_gone" : "true", "cat_asleep" : "true"} ) 'false' >>> interpret(["cat_asleep", "OR", ["NOT", "cat_gone"]], {"door_open" : "false", "cat_gone" : "true", "cat_asleep" : "true"}) 'true' >>> interpret(["true", "OR", "true"], {}) 'true' >>> interpret("cat_gone", {"door_open": "false", "cat_gone": "true"}) 'true' >>> interpret(["NOT", ["NOT", ["NOT", ["cat_asleep", "OR", ["NOT", "cat_asleep"]]]]], {"cat_asleep": "false"}) 'false'

2013-09-27: Ett par mindre förtydliganden i grönt har gjorts ovan.

3.6 En lite större uppgift

Uppgift 3C - Spelbräde

Tidigare har vi behandlat Game of Life, ett slags spel på ett relativt litet bräde, där vi på förhand inte vet riktigt hur många rutor som kommer att vara markerade. Låt oss nu säga att vi ska skriva ett spel där vi ska placera ut relativt få spelfigurer på ett väldigt stort bräde, säg en karta som bryts ned i 100 000x100 000 rutor.

En första tanke vore att göra som i det mindre problemet; att representera det hela som en matris av med listor i listor. En tom plats blir en nolla i matrisen och en figur markeras med ett spelarnamn, till exempel. Det kan ha sina fördelar, men fungerar inte så bra när skalan ökas. Säg att vi har fem spelare med 200 figurer var på kartan ovan. Det blir totalt 1000 symboler och inte mindre än 99 999 000 tomma rutor att lagra.

För att slippa lagra en massa tomma rutor explicit kan ett alternativ vara att knyta ihop varje figur och dess position på något sätt. På det sättet får vi 1000 saker att lagra istället för 100 000 000. Alla rutor som inte explicit har någon markering antas då vara tomma, och vi har sparat massor av minnesutrymme.

Din uppgift är att med hjälp av denna insikt, och den nyligen introducerade dictionary-typen, hitta på ett sätt att representera spelbrädet på ett smart sätt. I praktiken innebär det att du ska konstruera följande funktioner: reset_board, isfree (är en viss plats ledig?), place (placera en figur på en plats), get_piece (returnerar pjäsen på en viss plats), remove_piece, move_piece, count_pieces, nearest_piece. De ska fungera enligt det belysande exemplet nedan (där varje pjäs helt enkelt antingen är strängen "spelare1" eller "spelare2") :

>>> reset_board() >>> isfree(500, 100) # är plats (500, 100) ledig, dvs platsen på rad 500 och kolumn 100? True >>> place_piece(500, 100, "spelare1") # placera en figur från spelare1 på position (500, 100) True >>> place_piece(1, 100, "spelare2") True >>> place_piece(500, 100, "spelare2") # försök placera en figur på en redan upptagen position False >>> place_piece(500, 200, "spelare2") True >>> isfree(500, 100) False >>> get_piece(500, 100) 'spelare1' >>> get_piece(666,666) False >>> remove_piece(500, 100) # ta bort figuren på plats (500, 100) True >>> remove_piece(1, 1) # figuren finns inte False >>> isfree(500, 100) True >>> move_piece( 500, 200, 500, 100 ) # flytta pjäsen på (500, 200) till (500, 100) True >>> get_piece(500, 100) 'spelare2' >>> count("row", 500, "spelare2") # count antalet figurer av typen "spelare2" på rad 500 1 >>> count("column", 100, "spelare2") 2 >>> nearest_piece(500, 105) # hitta figuren närmast (500,105) (500, 100)

För att räkna ut avståndet mellan två koordinater i den sista funktionen ovan, använd Pythagoras sats. Du kan räkna med att de absolut vanligaste kommandona som spelet använder är isfree och get_piece.

Några tips

  • Läs genom listan på funktioner i exemplet ovan ordentligt. De visar vad du behöver kunna göra med spelbrädet. Testa dina idéer på representation mot dem. Funktionerna ska returnera precis det som står i exemplet. Försöker du använda samma nyckel flera gånger? Går det att se till att man behöver leta genom färre figurer? (Det finns många sätt att resonera här!)
  • Om du har skrivit och testat en funktion, använd när du bygger vidare (istället för att upprepa samma kod igen).

OBS! Denna kurs behandlar inte objektorientering. Om det är något du sysslat med tidigare (eller har råkat läsa om i boken), lägg inte tid på att kapsla in bräden och dylikt i klasser. Det ingår inte i uppgiften!

OBS! Från och med den här laborationen sker redovisning genom versionshanteringsverktyget Subversion. Läs mer om hur det fungerar rent praktiskt i vår introduktion.

När du har laddat upp samtliga uppgifter för laborationsomgång 3, kontakta din assistent (muntligen eller via e-post) och meddela att ni är klara. Assistenten kommer sedan att checka ut er kod och kolla igenom den. Återkoppling får ni sedan genom incheckade kommentarer.


Sidansvarig: Peter Dalenius
Senast uppdaterad: 2014-09-09