我正在装满一个装满拼字砖的袋子。在这些信件的袋子里应该有100个瓷砖:
A-9, B-2, C-2, D-4, E-12, F-2, G-3, H-2, I-9, J-1, K-1, L-4, M-2, N-6, O-8, P-2, Q-1, R-6, S-4, T-6, U-4, V-2, W-2, X-1, Y-2, Z-1 and Blanks-2.这些数字/字母永远不应该改变。我有一个可行的解决方案,但这很可怕。有很多重复的代码,有很大的错误空间,而且可读性很差。我很难理解如何使这段代码更易读、更容易出错。有什么建议吗?如果有人知道更好的方法的话,我不反对把这门课划破。
public class Bag
{
private Tile[] tiles = new Tile[100];
private const int numA = 9;
private const int numB = 2;
private const int numC = 2;
private const int numD = 4;
private const int numE = 12;
private const int numF = 2;
private const int numG = 3;
private const int numH = 2;
private const int numI = 9;
private const int numJ = 1;
private const int numK = 1;
private const int numL = 4;
private const int numM = 2;
private const int numN = 6;
private const int numO = 8;
private const int numP = 2;
private const int numQ = 1;
private const int numR = 6;
private const int numS = 4;
private const int numT = 6;
private const int numU = 4;
private const int numV = 2;
private const int numW = 2;
private const int numX = 1;
private const int numY = 2;
private const int numZ = 1;
private const int numBlank = 2;
public void populateTiles()
{
int i = 0;
for (int j = 0; j < numA; j++)
{
tiles[i] = new Tile('A');
i++;
}
for (int j = 0; j < numB; j++)
{
tiles[i] = new Tile('B');
i++;
}
for (int j = 0; j < numC; j++)
{
tiles[i] = new Tile('C');
i++;
}
for (int j = 0; j < numD; j++)
{
tiles[i] = new Tile('D');
i++;
}
for (int j = 0; j < numE; j++)
{
tiles[i] = new Tile('E');
i++;
}
for (int j = 0; j < numF; j++)
{
tiles[i] = new Tile('F');
i++;
}
for (int j = 0; j < numG; j++)
{
tiles[i] = new Tile('G');
i++;
}
for (int j = 0; j < numH; j++)
{
tiles[i] = new Tile('H');
i++;
}
for (int j = 0; j < numI; j++)
{
tiles[i] = new Tile('I');
i++;
}
for (int j = 0; j < numJ; j++)
{
tiles[i] = new Tile('J');
i++;
}
for (int j = 0; j < numK; j++)
{
tiles[i] = new Tile('K');
i++;
}
for (int j = 0; j < numL; j++)
{
tiles[i] = new Tile('L');
i++;
}
for (int j = 0; j < numM; j++)
{
tiles[i] = new Tile('M');
i++;
}
for (int j = 0; j < numN; j++)
{
tiles[i] = new Tile('N');
i++;
}
for (int j = 0; j < numO; j++)
{
tiles[i] = new Tile('O');
i++;
}
for (int j = 0; j < numP; j++)
{
tiles[i] = new Tile('P');
i++;
}
for (int j = 0; j < numQ; j++)
{
tiles[i] = new Tile('Q');
i++;
}
for (int j = 0; j < numR; j++)
{
tiles[i] = new Tile('R');
i++;
}
for (int j = 0; j < numS; j++)
{
tiles[i] = new Tile('S');
i++;
}
for (int j = 0; j < numT; j++)
{
tiles[i] = new Tile('T');
i++;
}
for (int j = 0; j < numU; j++)
{
tiles[i] = new Tile('U');
i++;
}
for (int j = 0; j < numV; j++)
{
tiles[i] = new Tile('V');
i++;
}
for (int j = 0; j < numW; j++)
{
tiles[i] = new Tile('W');
i++;
}
for (int j = 0; j < numX; j++)
{
tiles[i] = new Tile('X');
i++;
}
for (int j = 0; j < numY; j++)
{
tiles[i] = new Tile('Y');
i++;
}
for (int j = 0; j < numZ; j++)
{
tiles[i] = new Tile('Z');
i++;
}
for (int j = 0; j < numBlank; j++)
{
tiles[i] = new Tile(' ');
i++;
}
}
}public class Tile
{
private char letter;
private int value;
public Tile(char letter)
{
switch (letter)
{
case ' ':
value = 0;
break;
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
case 'L':
case 'N':
case 'S':
case 'T':
case 'R':
value = 1;
break;
case 'D':
case 'G':
value = 2;
break;
case 'B':
case 'C':
case 'M':
case 'P':
value = 3;
break;
case 'F':
case 'H':
case 'V':
case 'W':
case 'Y':
value = 4;
break;
case 'K':
value = 5;
break;
case 'J':
case 'X':
value = 8;
break;
case 'Q':
case 'Z':
value = 10;
break;
default:
Console.Error("Invalid Tile created!");
break;
}
}
}发布于 2015-06-20 20:52:17
抱歉,我只是想让事情变得更灵活,所以我必须更进一步.
我将在@EthanBierlein的版本和您的原始版本之间混合使用,并对其进行改进,进行以下更改:
在我看来,确实没有理由将某一字母的多少块配置分开,以及该瓷砖应该值多少个点。把它们都放在一个地方更合理,也更易于维护(考虑一下如果你想为其他语言拼字,不同的语言有不同的字母数量和不同的分数,更不用说不同的字母...了)
这将导致拼字游戏的设置更加可更改,您可以让A块具有不同的点值,您可以很容易地更改您在板中拥有的E块的数量,等等。您甚至可以扩展这个想法,并从配置文件中读取以确定每个方块应该有多少块。
我不是说你应该这么做,我只是说只要稍微修改一下代码,你就能做到这一点。
using System.Collections.Generic;
class Bag
{
public List<Tile> tiles = new List<Tile>();
/// <summary>
/// Populate the list tiles with scrabble tiles.
/// </summary>
public void PopulateTiles()
{
AddTile(9, 'A', 1);
AddTile(2, 'B', 42);
AddTile(2, 'C', 42);
AddTile(4, 'D', 42);
// ...
}
public void AddTile(int count, char letter, int score)
{
for (int i = 0; i < count; i++)
{
Tile tile = new Tile(letter, score); // Pass the score along to the tile as well
tiles.Add(tile);
}
}
}注意:方法名称应该使用PascalCase命名约定,而不是camelCase。从我的代码中可以看到,populateTiles应该是PopulateTiles。
发布于 2015-06-20 20:19:18
预警:我不是最有经验的C#程序员,所以如果有什么问题,只管提一下。
这里有太多无用的重复。这就需要使用System.Collections.Generic.Dictionary实现。您可以这样做来存储可能的瓷砖,以及它们的计数。
public Dictionary<char, int> possibleTiles = new Dictionary<char, int>() {
{'a', 9},
{'b', 2},
{'c', 2},
...
};然后你可以像这样翻阅这本字典。您将使用键作为要添加到您的tiles数组中的块,并使用该值作为这些特定瓷砖的数量。
foreach(KeyValuePair<char, int> item in possibleTiles)
{
// Add item.key item.value times to the tiles array.
}最后,作为参考,这里是my版本的Bag类。但是请注意,它使用的是List<Tile>而不是数组。
using System;
using System.Collections.Generic;
/// <summary>
/// Represents a bag of scrabble tiles.
/// </summary>
class Bag
{
public List<Tile> tiles = new List<Tile>();
private Dictionary<char, int> possibleTiles = new Dictionary<char, int>() {
{'a', 9}, {'b', 2}, {'c', 2}, {'d', 4}, {'e', 12},
{'f', 2}, {'g', 3}, {'h', 2}, {'i', 9}, {'j', 1},
{'k', 1}, {'l', 4}, {'m', 2}, {'n', 6}, {'o', 8},
{'p', 2}, {'q', 1}, {'r', 6}, {'s', 4}, {'t', 6},
{'u', 4}, {'v', 2}, {'w', 2}, {'x', 1}, {'y', 2},
{'z', 1}, {' ', 2}
};
/// <summary>
/// Populate the list tiles with scrabble tiles.
/// </summary>
public void PopulateTiles()
{
foreach(KeyValuePair<char, int> item in possibleTiles)
{
Tile tile = new Tile(item.Key);
int amount = item.Value;
for(int n = 0; n <= amount; n++)
{
tiles.Add(tile);
}
}
}
}发布于 2015-06-21 04:22:28
嗯,你似乎有很多重复:
for (int j = 0; j < numD; j++)
{
tiles[i] = new Tile('D');
i++;
}
for (int j = 0; j < numE; j++)
{
tiles[i] = new Tile('E');
i++;
}这两者之间有什么独特之处?为什么我们不能复制并粘贴它们呢?答案是numD\numE和'D'\'E'。
我们可以立即思考--想象一下,如果这些是值而不是硬编码的话?
List<Tile> tiles = new List<Tile>(); //For simplicity.
public void AddTiles(char letter, int count)
{
var newTiles = Enumerable.Range(0,count)
.Select(_ => new Tile(letter));
tiles.AddRange(newTiles);
}哇,太简单了!我们不必跟踪当前索引i、每个循环j的索引、需要添加多少块new Tile[100]或任何循环条件!
我们没有很好地跟踪这些值,所以让我们跟随西蒙·安德烈-福斯伯格的领导,并跟踪比分:
public void AddTiles(char letter, int count, int score)
{
var newTiles = Enumerable.Range(0,count)
.Select(_ => new Tile(letter, score));
tiles.AddRange(newTiles);
}哇!我们刚刚对程序的功能做了很大的修改,只需要添加一个参数!很简单吧?
不过,我还是不高兴。我们怎么打电话给AddTiles?是像这样吗?
public void PopulateTiles()
{
AddTiles(9, 'A', 1);
AddTiles(2, 'B', 3);
AddTiles(2, 'C', 3);
AddTiles(4, 'D', 2);
// ...
}我觉得不太对劲。它很长,很重复,而且很难编码--我们很难修改字典。如果我们有一个简单的方法来表示我们想要的瓷砖就好了?哦,等等,我们有!
A-9,B-2,C-2,D-4,E-12,F-2,G-3,H2,I-9,J-1,K-1,L-4,M-2,N-6,O-8,P2,Q-1,R-6,S-4,T-6,U-4,V-2,W-2,X-1,Y-2,Z-1和Blanks-2。
你已经有了一个简洁的方法来表达你的意图.那你为什么不用呢?为什么要重新发明轮子?
然而,我会做一些小的变化,以减少错误的风险,允许得分,并使‘空白’不那么神奇:
A:9:1,B:2:3,C:2:3,D:4:2,E:12:1,F:2:4,G:3:2,H:2:4,I:9:1,J:1:8,K:1:5,L:4:1,M:2:3,N:6:1,O:8:1,P:2:3,Q:1:10,R:6:1,S:4:1,T:6:1,U:4:1,V:2:4,W:2:4,X:1:8,Y:2:4,Z:1:10, :2:0
现在,这很容易修改和读取(尽管有了更多的时间/空间,我会使用换行符而不是,,或者json/csv来使阅读变得更干净)。
现在,我的程序看起来有点像这样:
const string defaultAlphabet = "A:9:1,B:2:3,C:2:3,D:4:2,E:12:1,F:2:4,G:3:2,H:2:4,I:9:1,J:1:8,K:1:5,L:4:1,M:2:3,N:6:1,O:8:1,P:2:3,Q:1:10,R:6:1,S:4:1,T:6:1,U:4:1,V:2:4,W:2:4,X:1:8,Y:2:4,Z:1:10, :2:0"
List<Tile> tiles = new List<Tile>(); //For simplicity.
public void Populate(string alphabet = defaultAlphabet)
{
foreach (var stringTile in alphabet.Split(','))
{
var parts = stringTile.Split(':');
AddTiles(parts[0], int.Parse(parts[1]), int.Parse(parts[2]));
}
}
public void AddTiles(string letter, int count, int score)
{
var newTiles = Enumerable.Range(0,count)
.Select(_ => new Tile(letter, score));
tiles.AddRange(newTiles);
}注意我们现在的代码有多少?这很好--因为如果我们想改变应用程序的行为,我们只需要修改几行代码--我们就没有多余的地方需要担心了。然而,在不改变任何代码的情况下,我们可以彻底改变所使用的字母表--甚至可以使它的用户可以改变!
https://codereview.stackexchange.com/questions/94202
复制相似问题