C# - Behöver hjälp med att skriva ett NUnit test.

Permalänk
Medlem

C# - Behöver hjälp med att skriva ett NUnit test.

Jag har en lista som innehåller 52 stycken instanser av en klass och jag skulle vilja skriva ett test som kollar ifall listan är fri från dubletter, några förslag på hur man kan göra det?

Permalänk
Medlem

Om klassen har en vettig equals metod så kan du loopa över alla 52 objekt och lägga dem i en hash map. Om ett objekt redan finns i mappen så har du en dubblet..

Kanske inte helt lätt att utföra eller förstå men det funkar.

Permalänk
Medlem

Du kan ta ett objekt och jämföra det mot alla andra objekt i listan, upprepa för alla objekt i listan. Kass pseudokod: http://pastebin.com/Up0ZyV9x

Permalänk
Medlem

Så här blev koden:

bool anyDoublets = false; Deck deck = new Deck(); List<Card> cardList = deck.Cards; for (int i = 0; i < cardList.Count; i++) { Card card = cardList[i]; cardList.RemoveAt(i); for (int j = 0; j < cardList.Count; j++) { if (card == deck.Cards[j]) anyDoublets = true; } } Assert.AreEqual(false, anyDoublets);

Den säger att det inte är några dubletter, hoppas jag inte gjort någon miss när jag skrev testet.

Permalänk
Medlem

Varför en lista när du kan använda SortedSet om jag får fråga?

Permalänk
Medlem
Skrivet av MrMadMan:

Varför en lista när du kan använda SortedSet om jag får fråga?

För att jag är nybörjare och inte har en aning om vad SortedSet är.

Permalänk
Medlem
Skrivet av Murloc:

Så här blev koden:

bool anyDoublets = false; Deck deck = new Deck(); List<Card> cardList = deck.Cards; for (int i = 0; i < cardList.Count; i++) { Card card = cardList[i]; cardList.RemoveAt(i); for (int j = 0; j < cardList.Count; j++) { if (card == deck.Cards[j]) anyDoublets = true; } } Assert.AreEqual(false, anyDoublets);

Den säger att det inte är några dubletter, hoppas jag inte gjort någon miss när jag skrev testet.

Att komma åt — läsa eller byta — ett element i en .NET-List är en snabb åtgärd; att lägga till ett element i slutet på listan är i princip lika snabbt. Däremot är det långsamt att ta bort element. Worst case-scenario är att ta bort från början av listan, vilket är det första som sker i din loop.

En tumregel är att det sällan är bra att mutera — förändra — en datastruktur, speciellt inte iterativt och definitivt inte samtidigt som datastrukturen itereras över. Dessa iterationer är ofta krångliga att tolka, då den data man arbetar mot ständigt förändras.

Efter att du kört din loop kommer deck.Cards, precis som cardList, vara en tom lista. Detta beror på att datastrukturen muteras.

Många programmeringsspråk framhållet vikten av immutability: att data aldrig kan förändras. Konceptet kan verka främmande (rentav skrämmande) till en början men ger renare kod som är lättare att följa och ofta ger effektivare program i slutändan. De flesta statiskt typade språk har någon form av nyckelord som låter programmeraren göra en lokal variabel immutable, exempelvis final i Java. Av orsaker jag verkligen inte förstår ges inte detta stöd i C#.

Här är en enkel lösning på ditt problem som inte muterar data:

Deck deck = new Deck(); Card[] cards = deck.Cards.ToArray(); bool anyDoublets = cards.Length == cards.Distinct().Count();

Notera att exemplet använder sig av LINQ; du kan behöva inkludera en referens i ditt projekt för att det ska fungera.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem
Skrivet av Teknocide:

Att komma åt — läsa eller byta — ett element i en .NET-List är en snabb åtgärd; att lägga till ett element i slutet på listan är i princip lika snabbt. Däremot är det långsamt att ta bort element. Worst case-scenario är att ta bort från början av listan, vilket är det första som sker i din loop.

En tumregel är att det sällan är bra att mutera — förändra — en datastruktur, speciellt inte iterativt och definitivt inte samtidigt som datastrukturen itereras över. Dessa iterationer är ofta krångliga att tolka, då den data man arbetar mot ständigt förändras.

Efter att du kört din loop kommer deck.Cards, precis som cardList, vara en tom lista. Detta beror på att datastrukturen muteras.

Många programmeringsspråk framhållet vikten av immutability: att data aldrig kan förändras. Konceptet kan verka främmande (rentav skrämmande) till en början men ger renare kod som är lättare att följa och ofta ger effektivare program i slutändan. De flesta statiskt typade språk har någon form av nyckelord som låter programmeraren göra en lokal variabel immutable, exempelvis final i Java. Av orsaker jag verkligen inte förstår ges inte detta stöd i C#.

Här är en enkel lösning på ditt problem som inte muterar data:

Deck deck = new Deck(); Card[] cards = deck.Cards.ToArray(); bool anyDoublets = cards.Length == cards.Distinct().Count();

Notera att exemplet använder sig av LINQ; du kan behöva inkludera en referens i ditt projekt för att det ska fungera.

Tack för att du pekade ut det. Om jag fattade det rätt så ska man undvika att förändra datastrukturer om det går att lösa på ett annat sätt, för att det tar mycket prestanda. Vad sägs som den här lösningen som använder en variabel för att hålla koll på vilka objekt i listan som inte ska kollas?:

bool anyDoublets = false; List<Card> cardList = deck.Cards; int boundary = 0; for (int i = 0; i < cardList.Count; i++) { Card card = cardList[i]; boundary += 1; for (int j = 0; j < cardList.Count; j++) { if (j >= boundary) { if (card == deck.Cards[j]) anyDoublets = true; } } } Assert.AreEqual(false, anyDoublets);

Nu förändras listan aldrig, problemet löst?

Permalänk
Medlem
Skrivet av Murloc:

Tack för att du pekade ut det. Om jag fattade det rätt så ska man undvika att förändra datastrukturer om det går att lösa på ett annat sätt, för att det tar mycket prestanda. Vad sägs som den här lösningen som använder en variabel för att hålla koll på vilka objekt i listan som inte ska kollas?:

bool anyDoublets = false; List<Card> cardList = deck.Cards; int boundary = 0; for (int i = 0; i < cardList.Count; i++) { Card card = cardList[i]; boundary += 1; for (int j = 0; j < cardList.Count; j++) { if (j >= boundary) { if (card == deck.Cards[j]) anyDoublets = true; } } } Assert.AreEqual(false, anyDoublets);

Nu förändras listan aldrig, problemet löst?

Det finns många fördelar med immutability. Prestandan behöver inte bli lidande för att man muterar datastrukturer men i ditt första exempel blev det en massa overhead då din lista behövde byggas om varje gång du körde RemoveAt på den (och dessutom plockade den bort kort lite underligt när jag tänker efter; först kort 0 (första), sedan kort 1 (dvs tredje kortet i original-Deck:en) sedan kort 2 (femte). Ett väldigt bra exempel på att det är svårare att följa det logiska flödet i en iterativ loop som muterar.

Du har fortfarande två variabler som muteras: boundary och anyDoublet. Faktum är att boundary är överflödig eftersom den håller samma värde som din i-variabel. En annan optimering du bör göra är att låta j börja på i+1. Detta gör att du kan skippa din inre if-sats. Ytterligare en optimering är att låta koden bryta sig ur looparna samtidigt som anyDoublets sätts till true: vid det tillfället garanteras att det finns minst en dublett och eftersom antalet dubletter är ointressant behöver ingen vidare kontroll av korten göras.

Utöver detta refererar cardList och deck.Cards till samma lista: du kan ersätta cardList med deck.Cards överallt.

Om du stoppar in hela kodsnutten i en metod blir du av med den sista muterande variabeln (bortsett från i och j) samtidigt som du kan bryta dig ur looparna:

bool AnyDoublets(List<Card> deck) { for (int i=0; i<deck.Count; i++) { for (int j=i+1; j<deck.Count; j++) { if (deck[i] == deck[j]) return true; } } return false; }

Eller, om man tycker att det är snyggare utan alla måsvingar (och gillar att spexa med generics och interfaces ):

bool AnyDoublets<T>(ICollection<IEquatable<T>> collection) { for (int i=0; i<collection.Count; i++) for (int j=i+1; j<collection.Count; j++) if (collection[i] == collection[j]) return true; // default: return false; }

PS: Du måste som sagt implementera IEquatable<T>/en .Equals-metod på din Deck också, jag utgår ifrån att där finns en sådan. Om inte kommer du med stor sannolikhet aldrig hitta några dubletter.

edit: uppdatering, du måste använda antingen använda .Equals uttryckligen, eller overloada ==-operatorn. Om inte detta görs kommer du jämföra referenser med referenser, och inte objektets själva värde.

edit2: fler uppdateringar. Grymt trött nu.

edit3: god morgon! AnyDoublets (Doublet är förresten ett klädesplagg ) borde vara en metod på Deck-objektet. Då behöver du bara köra (cards.AnyDoublets()) för att få veta om där finns dubletter.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem
Skrivet av Murloc:

För att jag är nybörjare och inte har en aning om vad SortedSet är.

SortedSet (sorterad mängd på svenska) är en "lista" som garanterar att du inte kan stoppa in mer än ett av varje element. Den kommer alltså inte att tillåta dig att lägga in dubletter från första början.

Permalänk
Medlem
Skrivet av Teknocide:

Det finns många fördelar med immutability. Prestandan behöver inte bli lidande för att man muterar datastrukturer men i ditt första exempel blev det en massa overhead då din lista behövde byggas om varje gång du körde RemoveAt på den (och dessutom plockade den bort kort lite underligt när jag tänker efter; först kort 0 (första), sedan kort 1 (dvs tredje kortet i original-Deck:en) sedan kort 2 (femte). Ett väldigt bra exempel på att det är svårare att följa det logiska flödet i en iterativ loop som muterar.

Du har fortfarande två variabler som muteras: boundary och anyDoublet. Faktum är att boundary är överflödig eftersom den håller samma värde som din i-variabel. En annan optimering du bör göra är att låta j börja på i+1. Detta gör att du kan skippa din inre if-sats. Ytterligare en optimering är att låta koden bryta sig ur looparna samtidigt som anyDoublets sätts till true: vid det tillfället garanteras att det finns minst en dublett och eftersom antalet dubletter är ointressant behöver ingen vidare kontroll av korten göras.

Utöver detta refererar cardList och deck.Cards till samma lista: du kan ersätta cardList med deck.Cards överallt.

Om du stoppar in hela kodsnutten i en metod blir du av med den sista muterande variabeln (bortsett från i och j) samtidigt som du kan bryta dig ur looparna:

bool AnyDoublets(List<Card> deck) { for (int i=0; i<deck.Count; i++) { for (int j=i+1; j<deck.Count; j++) { if (deck[i] == deck[j]) return true; } } return false; }

Eller, om man tycker att det är snyggare utan alla måsvingar (och gillar att spexa med generics och interfaces ):

bool AnyDoublets<T>(ICollection<IEquatable<T>> collection) { for (int i=0; i<collection.Count; i++) for (int j=i+1; j<collection.Count; j++) if (collection[i] == collection[j]) return true; // default: return false; }

PS: Du måste som sagt implementera IEquatable<T>/en .Equals-metod på din Deck också, jag utgår ifrån att där finns en sådan. Om inte kommer du med stor sannolikhet aldrig hitta några dubletter.

edit: uppdatering, du måste använda antingen använda .Equals uttryckligen, eller overloada ==-operatorn. Om inte detta görs kommer du jämföra referenser med referenser, och inte objektets själva värde.

edit2: fler uppdateringar. Grymt trött nu.

edit3: god morgon! AnyDoublets (Doublet är förresten ett klädesplagg ) borde vara en metod på Deck-objektet. Då behöver du bara köra (cards.AnyDoublets()) för att få veta om där finns dubletter.

Kul att du lade ner så mycket tid på ditt inlägg. Sett från en nybörjares perspektiv verkar det här med "immutability" ganska jobbigt. Och jag borde ha sett att jag kunde använda i istället för att göra en ny variabel, det tar ett tag att få in det rätta programmeringstänket.

Det här med en .Equals metod har jag ingen aning om vad det är eller varför jag behöver den, menar du att koden jag skrev i mitt förra inlägg både var ful kod och inte kan hitta dubletter?

Skrivet av Teknocide:

AnyDoublets (Doublet är förresten ett klädesplagg )

Det kom jag på kort efter att jag skrivit variabelns namn. Men genom ordbok kom jag fram till att en annan betydelse av doublet är "a set of two identical or similar things"