Kryptokorsordslösare Programmeringsmetodik DV1 2004 (period 2) Inlämningsuppgift 1 Christer Folkesson 1. Sammanfattning 2. Användarbeskrivning 2.1. Lösa ett kryptokorsord 2.2. Utskrift av lösning 2.3. Körexempel 3. Programdokumentation 3.1. Abstrakta datatypen match 3.2. Funktioner för utförande av algoritmen 3.3. Algoritm 4. Kända brister, mindre lyckade saker etc. 4.1. createbond i abstrakta datatypen 4.2. createemptymatch i abstrakta datatypen 4.3. matchwordlist och matchestowordlist 1. Sammanfattning Programmets syfte är att systematiskt lösa ett kryptokorsord m.h.a. en ordlista. Alla möjliga kombinationer undersöks och resultatet visar om det finns någon eller några lösningar sett från den ordlista som använts. En abstrakt datatyp hanterar lagring av möjliga bindningar mellan bokstäver och siffror och den svarar även för att kunna presentera resultatet i läslig form. 2. Användarbeskrivning För att kunna använda programmet måste man på ett korrekt sätt representera sitt kryptokorsord samt ordlista. I SML är det presentation av en int list list datatyp för kryptokorsordet. En lista av heltal beskriver ett kryptoord och en lista av sådana är hela kryptokorsordet. För ordlistan används en string list list datatyp. I varje stränglista så är orden lika långa, dvs strängarna har samma längd. En lista av sådana listor skapar en ordlista. 2.1. Lösa ett kryptokorsord För att försöka lösa ett kryptokorsord så används funktionen: solve(kryptokorsord,ordlista) Funktionen returnerar en lista av matchningar. Det är alltså den abstrakta datatypen som ligger i listan. Men eftersom den interna representationen av datan är hemlig så ser man inte resultatet på något vis. 2.2. Utskrift av lösning För att kunna förstå lösningen så har den abstrakta datatypen en funktion kallad frommatch(kryptolösning) Som skriver ut en enkel visning med radbrytning mellan varje bindning. Observera att kryptolösning inte ska vara en lista av lösningar (som solve ger ifrån sig) utan bara ett element ur en sådan lista (en matchning).
2.3. Körexempel En väldigt simpel visning: solve([[1,2,3],[4,5,3,3]], [[ GRÅ, GUL, BLÅ, VIT ], [ KATT, MULA, ÅSNA ]]); Ger i mosml följande: > val it = [<match>] : match list Vi ser att det finns en lösning (ett element), men för att kunna läsa vad lösningen blev så får man köra den lösningen från solve genom frommatch. För att inte behöva skriva så mycket kör vi bara: frommatch(hd(it)); Där it är resultatet från solve som kördes före (det går givetvis bra att baka ihop alltihopa till ett uttryck och köra). Utskriften från frommatch blir: 5=A 4=K 3=T 2=I 1=V ----- Skulle frommatch få en tom lista så skrivs bara den sista raden som visas ovan ut (5 bindestreck). 3. Programdokumentation 3.1. Abstrakta datatypen match För lagring av bindningar mellan bokstäver och siffror från kryptokorsordet används en abstrakt datatyp som då kan uppräthålla invarianten för lagringen. Konstruktorn för är: MatchType of (int*char) list Datan lagras alltså internt som en lista av int*char tuppler. Det ska alltid råda ett 1:1 förhållande, dvs bara ett number kan vara bundet till en bokstav och bara en bokstav kan vara bunden till ett number. Tre primitiver och 2 andra funktioner finns för användande av datatypen (absobjekt är ett fiktivt objekt av match): Namn Primitivtyp Beskrivning getmatchn (number, absobjekt) Selektor Kontrollerar om talet number finns lagrat och returnerar bokstaven som den är bunden till eller säger att number inte finns lagrat tidigare. getmatchc (letter, absobjekt) Selektor Kontrollerar om bokstaven letter finns lagrat och returnerar numret som den är bunden till eller säger att letter inte finns lagrat tidigare. createemptymatch () (ingen) Returnerar (skapar) en tomt objekt av match abstrakta datatypen. frommatch (absobjekt) (ingen) Skriver ut allt innehåll i absobjekt i läslig form. Se (2.3) för en exempelutskrift. createbond (number, letter, absobjekt) Konstruktor Skapar en bindning mellan number och letter om någon av dem inte tidigare är lagrad och returnerar det inbakat i absobjekt. Är de redan med så lagras de inte och absobjekt returneras som det var från början.
3.2. Funktioner för utförande av algoritmen Det finns bara en extern funktion, solve. Den definierar internt ett antal rekursiva funktioner och anropar en av dem för att börja lösa kryptokorsordet. En listning av funktionerna och kort beskrivning av dem: findwordlist (kryptoord, ordlistor) matchword (kryptoord, ord, kryptomatch) matchwordlist (kryptoord, ordlista, kryptomatch) matchestowordlist (kryptoord, ordlista, kryptomatches) matchkryptolist (kryptolista, ordlistor, krypomatches) En hjälpfunktion som väljer ut listan med strängar ur ordlistor som är lika långa som kryptoordet är. Det minsta kugghjulet, det som snurrar mest så att säga, den anropar ingen av de andra funktionerna i solve. Uppgiften det har är att kontrollera om kryptoord och ord passar ihop, dels på siffror och bokstäver men även ev. tidigare bindningar i kryptomatch. Om de passar ihop returneras en lista med ett element med just den matchningen, annars är listan tom. Funktionen anropar matchword för varje ord i ordlista med samma kryptoord och kryptomatches i alla anrop. Returvärdet är en lista med alla (ev. inga) möjlig utvecklingar som kan göras. Funktionen anropar matchwordlist. En lång lista byggs upp av möjliga lösningar (matchningar) och de (från kryptomatches) som inte klarar testet försvinner. Funktionen som solve anropar med en tom matchning. Denna ansvarar för att välja en lämplig ordlista via findwordlist till anropet för matchestowordlist som returnerar ett svar som används när matchkryptolist rekursivt anropar sig självt. Kopplingarna mellan alla funktioner har jag också åskådliggjort med en figur på sista sidan. 3.3. Algoritm Tankesättet från den ursprungliga algoritmen jag gjorde är inte på pricken likadan som implementationen blev tillslut, en del var jag tvungen att ändra efterhand men det stora som algoritmen beskriver på pappret stämmer. Ursprungliga algoritmen: Algoritmbeskrivning (anm. kryptoord är de sifferföljder från kryptot, ord är sådana som finns i ordlistan) : 1. Ta fram en lista (ett kryptoord) i taget. 2. Ta ett kryptoord och sök igenom ordlistan efter ord av samma längd med möjliga matchningar av bokstäverna och siffrorna. 3. För varje funnen matchning i steg 2 så upprepa steg 2 på ett annat kryptoord fast med hjälp av bindningen av siffror och bokstäver från tidigare kryptoord, upptäcks det att en matchning saknar lösning, släng den. 4. Gör steg 2 och 3 tills alla siffror är ihopparade med varsin bokstav (ev flera matchningar kvar). Fortsätt testningen på ev resterande ord med samma sökning. 5. Returnera giltiga matchningar från steg 4 och presentera dem. Steg 1 sker i matchkryptolist, steg 2 består både av findwordlist, matchword, matchwordlist och matchestowordlist. Steg 3 är det återigen matchkryptolist som ytterst är inblandat (när kryptoordet byts). Steg 4 sköter sig själv tills alla kryptoord är testade. Slutgiltigen steg 5; nog returnerar solve matchningarna men uppgiftsbeskrivningen anger att en annan funktion (frommatch, i den abstrakta datatypen) ska presentera innehållet. Så det ligger alltså utanför vad algoritmen egentligen skulle göra och är således inte med inom solve:s funktioner.
4. Kända brister, mindre lyckade saker etc. Programmet klarar av allt det ska klara av men några saker som jag är mindre nöjd med eller tänker mig att de kunnat utföras bättre är följande: 4.1. createbond i abstrakta datatypen Funktionen rapporterar inte på något sätt tillbaka om huruvida en bindning skapades eller inte. Det blir alltså inte entydigt bestämt utifrån resultatet om den exakta bindningen man vill skapa redan fanns eller om den skapades. Matchningen som returneras måste även dissekeras för att se om den blev någon uppdatering eller inte. Den här kontrollen över om sakerna redan finns osv. har då hamnat utanför (i matchword). Kort sagt så ser den till att datastruktursinvarianten bibehålls men hjälper inte till med något annat. 4.2. createemptymatch i abstrakta datatypen Jag är inte helt säker på att den här funktionen är nödvändig men jag kom inte på något annat sätt att lösa det på. Jag kunde inte använda konstruktorn för datatypen eftersom det är abstype och jag behövde en tom matchning att börja med när matchkryptolist anropas. 4.3. matchwordlist och matchestowordlist Dessa funktioner ligger nära varandra i vad de utför och jag försökte få en funktion att utföra samma uppgift som dem båda men jag fick dela upp arbetet. Rekursionen i matchestowordlist är jag inte säker på att den är optimal. Ev. kanske det går snabbare om man vänder på funktionsanropen runt append (@), dvs. rekursiva anropet först isf.
Abstrakta datatyper createemptymatch() getmatchn (number, MatchType(mList)) getmatchnaux (number, mlist) abstype match = MatchType of (int*char) list getmatchc (letter, MatchType(mList)) getmatchcaux (letter, mlist) frommatch (MatchType (mlist)) createbond (number, letter, MatchType(mList)) Externa funktioner solve (kryptolista, ordlistor) matchkryptolista (kryptolista, ordlistor, kryptomatches) matchestowordlist (kryptoord, ordlista, kryptomatches) findwordlist (kryptoord, ordlistor) matchwordlist (kryptoord, ordlista, kryptomatch) matchword (kryptoord, ord, kryptomatch)