保龄球
我觉得,作为一名程序员,尤其是一名优秀的程序员,并不是要看多少程序书,懂多少艰涩的程序知识,而应该是拥有丰富的实践经历以及从这些经历中提取出来的教训和方法,这才是衡量一名程序员好坏的标准,更是衡量他是否能在业界混下去的必备能力。因为程序员解决的是实践中的问题,就是用程序来解决实践中我们遇到的思维难点。什么叫做思维难点,就是我们在程序设计中遇到的一些想要在这里实现但是却无法实现的技术问题。这些问题的解决,就能大大增进我们的实践能力,因为这样的问题肯定背后有着相关技术或者技巧的支持,而这种打破砂锅问到底的精神就能让我们从实践中的一个小小缺口渗进到更大的层次里。
现在来说说我现在遇到的思维难点。这个难点,我现在归纳如下:
List中按照每两个元素相加,再将得到的和存进另一个List中,这样的过程有三种情况:
(1)如果这两个元素加起来的和不是10,将只是将它们加起来而已;
(2)如果这两个元素加起来的和是10,那么加起来的和还得将下一个元素加起来;
(3)如果第一个元素就是10,那么,加起来的和就要将下面两个元素也加起来;
这里的情况还有边界的检查问题,就是如果接下来的元素没有的话,那么像上面的(2),(3)情况,就可能越界了。所以边界检查是在所难免的。
我承认,这个问题我现在解决不了,因为情况太多,明明我真的已经将所有的情况都兼顾了,但是却陷入了一种测试单元无法进行,一直在跑的困局,还有就是栈溢出。哈哈,问题真多,但是我觉得这个问题的解决还是非常有意义的,甚至里面的一些东西能够帮助我更好的理解循环的设计,因为循环真的很重要,在程序设计中经常要用到循环,如果设计不好,就会使程序出现问题。所以,良好的循环设计能够使我们的程序的功能更加健壮。
这里还有一个需要理清楚的地方,遇到问题时,如果思路遭到阻碍,该怎么办?怎样才能抓住问题的本质呢?或者怎样才能理清自己的问题呢?理清楚问题是很重要的,因为当在理清楚问题的过程中,已经将这个问题的本质抓到了,而且可能这个过程中,就已经找到解决问题的思路了。
接下来,就是如何才能更有效的将问题理清楚?如果是错误的方法只会使我们更加混乱。现在我就试试吧,将我的思路写出来:
定义两个List--->将所有的元素填进其中一个List--->遍历这个List,首先就是先要判断第一个元素是否是10,如果是10,那么就将下面两个元素加起来,这个过程是要不断判断接下来的元素是否是10,这个过程还要判断下面是否还有元素,因为有可能这局就是最后一局。如果不是10,那么就要判断加起来是否是10,如果是10,就要将接下来的元素加起来,还是要判断是否是是最后一局。不是10的话,只要加起来就行。很简单的步骤,但是为什么就会发生所谓的测试一直都在运行的问题呢?只能是我的循环出现问题了。那么,就按照这样的思路开始写代码吧。
开始写代码,就开始出现问题了,循环出现问题了,越界了,这里的问题是循环的上限值出现问题,到界了,size是2,index是2,确实,想想就发现我错了,因为这里是i+1,如果没有将界限设为size-1,那么在到达size这个值时,就会发生index等于size的错误。唉,我太糊涂了!
问题继续出现,怎么回事,我的测试竟然卡住了!为什么!测试的情况是第3种情况,测试一直没有出现结果。想不明白,因为这个并没有报错啊!无法看到结果,自然就无法知道怎么办!
这时就要沉住气了,在每个程序的逻辑点设置测试点。所谓的逻辑点,就像循环的入口,或者if语句等等,测试点可以是输出语句,看看有没有执行到这里。我通过这样的方法就知道我的代码是执行到哪里,哪里没有执行了。这种方法对于循环和大量的if-else语句来说是非常有用的。问题现在又是越界,是的,循环还有对i的任何操作都可能会出现这样的错误。
呵呵,解决一个问题。另一个问题就接踵而来。这个问题就是i的递增值的操作无法执行正确,因为我这个问题是有两种i的递增方式,所以i这里就用一个方法,但是显然我这个方法并没有正确发挥它该有的作用。原因很快就可以知道,妈的,我这个方法只是返回一个值,并没有赋给i!唉,实在太粗心了!
经过测试,又发现它无法通过连续10的测试,会出现多加10的现象。好吧,又得努力了。
好了,现在确实是通过了10,10,2,3的测试,但是却没有通过10,10,10,2,3的测试,这其中又有问题了。唉,现在该怎么办了?这问题后来也顺利解决了,就是关于确定后面有多少个10的地方出现点问题。
接下来就是程序结构的整理了。因为一开始写的时候单纯只是为了通过测试,所以有些地方写的有点乱,必须好好整理一下。在这个过程中,我去掉了一些方法,因为它们可以与其他方法合并在一起,比如说,计算后面一个10还是多个10的情况,我一开始分了出来,因为多个10后面是需要知道有多少个10的,所以,就又多了个方法,就是得到后面有多少个10的信息,但是后来发现,其实一个和多个的算法都是一样的,只是多了个方法,而且这个方法对于一个10也是适用的。所以,这对于我来说,是个提醒,解决一个还是多个的问题原本在本质上就是一样的,如果解决多个的算法无法适用于解决一个的话,那这个算法就一定是错的。所以,我认为,遇到这个问题,不要像我之前那样,先从一个开始下手,而应该一开始就是从解决多个的情况开始。然后就是开始顺着程序的过程阅读代码,如果觉得方法的名字和实现都是一致的话,那么说明这个方法是符合要求的 。这里,我有一个绕不开的问题,就是所有的方法都有索引值作为参数,对于这个问题,也算不上问题,就是觉得有点烦,但是又实在是想不出什么问题来解决,于是就只好搁在一边,如果有人看了我的代码,能有什么解决的方法还请告诉我,因为对于参数列表一样的方法,我总觉得可以合并,但是我里面很多都是辅助方法,像是检查边界,判断是否是10,接下来是否是10,等等,实在找不到合并的地方。通过这次实践,我还发现,一开始我是将边界分开的,就是有两种情况,因为在我的算法中,一个是有i+1,另一些是没有的,但是后来发现,其实去掉又何妨。这说明什么?说明,如果你的代码中需要将边界划为两种情况,说明你的代码中一定是哪里有问题,因为边界这种东西是几乎不会需要分情况出来的。
好了,就先写到这里,如果我有什么想法再说吧。代码如下:
package pratice; import java.util.ArrayList; import java.util.List; public class Game { List<Integer> bottleList = new ArrayList<Integer>(); List<Integer> scoreList = new ArrayList<Integer>(); public void add(int score) { bottleList.add(score); } public int getScore() { int score = 0; for (int i = 0; i < scoreList.size(); i++) { score += scoreList.get(i); } return score; } public void calScore() { int i = 0;
while(i < bottleList.size()){
if(isTen(i)){
CalScoreIfTen(i);
i++;
}
else{
CalScoreIfNotTen(i);
i += 2;
}
}
} private void calScoreIfNotTen(int i) { int score = 0; if ((i + 1) < bottleList.size()) { score = bottleList.get(i) + bottleList.get(i + 1); if (score == 10) { if (!(isReachEdge(i))) { score += bottleList.get(i + 2); } } } scoreList.add(score); } private void calScoreIfTen(int i) { int score = 0; int index = isNextTen(i); if (isReachEdge(i)) { score = bottleList.get(i); } else { if ((index + 1) < bottleList.size()) { score = (index - i) * 10 + bottleList.get(index + 1) + bottleList.get(index); } else { score = (index - i) * 10 + bottleList.get(index); } } scoreList.add(score); } private int isNextTen(int i) { while (bottleList.get(i) == 10) { i++; } return i; } private boolean isReachEdge(int i) { boolean isreachEdge = false; if (i == bottleList.size()) { isreachEdge = true; } return isreachEdge; } private boolean isTen(int i) { return (bottleList.get(i) == 10); } }
测试代码如下:
package pratice; import junit.framework.TestCase; public class TestBowling extends TestCase { public void test1() { Game g = new Game(); g.add(2); g.add(3); g.calScore(); assertEquals(5, g.getScore()); } public void test2() { Game g = new Game(); g.add(5); g.add(5); g.add(2); g.add(3); g.calScore(); assertEquals(17, g.getScore()); } public void test3() { Game g = new Game(); g.add(10); g.add(2); g.add(3); g.calScore(); assertEquals(20, g.getScore()); } public void test4() { Game g = new Game(); g.add(10); g.add(10); g.add(10); g.add(2); g.add(3); g.calScore(); assertEquals(80, g.getScore()); } public void test5() { Game g = new Game(); g.add(1); g.add(4); g.add(4); g.add(5); g.add(6); g.add(4); g.add(5); g.add(5); g.add(10); g.add(0); g.add(1); g.add(7); g.add(3); g.add(6); g.add(4); g.add(10); g.add(2); g.add(8); g.add(6); g.calScore(); assertEquals(133, g.getScore()); } }