MIT OpenCourseWare
OCW Home Course List About OCW Help with OCW Feedback
搜索

Search
» 高级搜索
 课程主页
 教学大纲
 教学日程
 参考读物
 讲义
 复习
 实验
 作业
 考试
 项目
 工具
 相关资源

练习 2


进度: 第三周

介绍

在这些问题中,你将练习阅读和解释规格说明,同时还要练习Java™源代码的阅读和编写。你需要实现两个类来完成图形化多项式计算器的实现,并回答关于给出的代码以及你所编写的代码的问题。

要完成本节中的问题,你需要了解

  • 代数基础(有理数和多项式计算)
  • 如何阅读程序的规格说明(需求、更改、结果) 以及表示不变式(representation invariants或者rep invariants)。
  • 怎样阅读和编写基本的Java™代码
    •  代码的结构和布局(类和方法的定义,属性和变量的声明)
    • 方法调用
    • 控制结构:循环(while和for语句)和条件转移(if、then、else语句)
    • 下列操作运算符:
      • 创建对象:new
      • 属性和方法调用:.
      • 赋值:=
      • 比较:==, !=, <, >, <=, >=
      • 数学运算:+, -, *, /
  • 怎样使用Java™c之类的Java™编译器来创建 “.class”文件
  • 怎样运行Java这样的Java™虚拟机(JVM)来执行你的代码或我们提供的库。


问题1: RatNum [15 分]

阅读关于老鼠数量问题的说明,一个类用来表示有理数。接着请阅读我们提供的问题实现,RatNum.java。仔细阅读RatNum类 使用实例里面RatNumTest.java中的代码可能会对你非常有帮助(虽然这只是一段测试驱动代码,而不是程序的实现代码)。回答以下问题,并将你的答案保存为problem1.txt。

add, mul, sub, div等方法之前的一行注释指明了什么?注意,add、mul、sub、div等方法都要求“arg != null”。这是因为任何方法调用时都不会检查‘arg’是否为空值。但是对‘this’属性的访问也不会进行非空检查,为什么在方法的注释中没有“this != null”的描述。为什么RatNum.div(RatNum)检查它的变量是否NaN(非数字)而RatNum.add(RatNum)和RatNUm.mul(RatNum)却不进行这些检查。为什么RatNum.parse(String)是一个静态方法?对于静态方法,什么样的备选方案能够完成从一个输入字符串得到RatNum? 假设representation invariant被削弱了,因此我们就不必要求number和denom属性以简化形式存储。这也就是说在方法的实现中我们不能再假设常量保存在方法的入口,但是将常量保存在出口也不再是必须的了。因此新的rep invariant应该是:

// 对于所有RatNum的Rep Invariant r: ( r.denom >= 0)

哪些方法和构造器的实现应该修改?对于每段代码的修改,简单的介绍都进行了哪些改变,并且请指出这些改变将对结果的复杂性带来什么变化(包括代码的清晰性和程序执行的效率)。注意,新的实现仍必须满足给出的规格说明;尤其是RatNum.unparse()需要输出简化形式的片断。

问题 2: RatPoly [45 分]

阅读RatTerm、RatTermVec和RatPoly类的规格说明。我们已经给出了RatTerm、RatTermVec类的实现(你就不需要再编写它们的代码了)。请确认你已经搞清楚了RatPoly类以及给出方法的说明。阅读我们在RatPoly.java中给出的RatPoly类实现的总体结构。我们给出的文档中最重要的部分就是关于你该如何使用给出的属性来实现RatPoly类的注释。Rep. Invariant的注释是非常重要的,因为你所定义的不变式将对RatPoly类中各个方法该如何实现有非常大的影响。

将RatPoly类说明中的各个方法补充完整。如果需要,你可以自己定义一些你认为有用的方法;我们提供了一些我们所定义的方法以及它们的说明,但是你不一定要使用它们(但是我们认为如果使用它们将大大简化你的工作)。

在RatPolyTest.java中我们也提供了一组相当严格的测试用例。你可以使用JUnit运行这些测试用例来检验你的程序,以评价你的成果和你的代码的正确性。使用下列命令来运行RatPoly测试用例:

athena% java junit.swingui.TestRunner ex2.RatPolyTest

如提示中指出的,对于非可视化的JUnit,使用junit.textui.TestRunner来代替junit.swingui.TestRunner。

明确的说明你的代码是否通过了所有的测试。如果你什么都不说,我们会假设你的代码没有通过测试。


问题 3:RatPolyStack [25 分]

按照问题2中所给的过程,不过这次需要补充完整的是RatPolyStack.java。仍遵循问题2中所给出的规则(你仍可以自己定义一些你认为有用的方法)。

在RatPolyStackTest.java中我们也提供了一组相当严格的测试用例。你可以使用JUnit运行这些测试用例来检验你的程序,以评价你的成果和你的代码的正确性。使用下列命令来运行RatPolyStack测试用例:

athena% java junit.swingui.TestRunner ex2.RatPolyStackTest

明确的说明你的代码是否通过了所有的测试。如果你什么都不说,我们会假设你的代码没有通过测试。

问题 4:PolyCalc [5 分]

在完成了系统中遗留的两个类的实现之后,你现在可以运行PolyCalc了。它允许你通过点击用户接口输入一些多项式并对它们进行运算。计算器程序完成计算并绘制计算的结果。输入以下命令来运行PolyCalc:

athena% java ex2.PolyCalcFrame

程序开始运行后,将弹出一个窗口,它的左侧有一个栈、右侧有一个图形显示器、一个用于输入多项式的文本框,在这个窗口的底部还有一些按钮。通过点击不同按钮来输入多项式或对栈中的多项式进行操作。图形显示器将根据栈的变换而更新,始终显示栈中最上面的四个元素。

将你最中意的4个多项式以RatPoly.unparse格式保存在problem4.txt中提交。

问题 5:模块依赖图 [10  分]

绘制一个在最终运行程序中使用所有类的接口的模块依赖图,其中应包括它们的依赖和相应的关系。图中不用包括PolyCalcFrame。你应该选择一个合适的Java™类库。

你(和你的助教)可能会觉得使用Visio工具将对绘制MDD(模块依赖图)非常有帮助。Visio使用的详细资料请查看使用工具一章的讲义。

我们提供的类:

我们提供了以下的已经编译成功的类:

  • 包括源代码的类:
    ex2.RatNum
    ex2.RatNumTest
    ex2.RatPolyTest
    ex2.RatPolyStackTest
    ex2.Cons (as part of the RatPolyStack.java starter code)
  • 没有源代码的类:
    ex2.PublicTest
    ex2.PolyGraph
    ex2.PolyCalcFrame
    ex2.RatTerm
    ex2.RatTermVec

开始

在开始开发之前,请确认你已经按照使用工具一章所介绍的方法正确的安装并使用了Java™。

创建一个ex2目录,并将我们提供的文件拷贝到里面:

mkdir ~/6.170/ex2
cp -p /mit/6.170/www/psets/ex2/ex2-spec/* ~/6.170/ex2

接着,将规格说明文件编辑到你的实现中,并测试它们。你不需要编译我们提供的源代码,在6.170课程中我们给出了所有我们提供的编译成功后的类文件。请阅读需要你完成的以及我们给出的所有类的规格说明。

在问题的最后,你应该提交以下保存在ex2目录下的文件:problem1.txt,RatPoly.java,RatPoly.java,RatPolyStack.java以及problem4.txt,还应包括你所提交的文件的一个清单。

提示

  • 阅读问题指南和提示。
  • 请先认真思考问题之后再开始编码!多项式运算函数并不困难,但是如果你没有制定一个良好的计划就匆匆开始,你的开发将很容易陷入混乱的困境之中。
  • 每次你运行测试时,JUnit将重新装载所有你编写的类,所以在改变代码后不需要重新启动JUnit(但是你需要重新编译你的代码来使你所做的改变生效)。
  • 对于非可视化的JUnit,使用junit.textui.TestRunner来代替junit.swingui.TestRunner。
  • 我们提供的问题1的测试用例将被用来给你的实现评分。而在以后的问题中我们不会再提供一个非常全面的测试用例来检验你的实现,但是对于本节的问题,我们提供的测试用例已经足够了,因此你不需要再编写你自己的测试用例。
  • 有理数之上的多项式除法和小学所学到的长除法一样。

勘误

我们提供的RatPoly.java实现中有一个小错误。这将导致学生的测试用例由于NaN问题而无法通过,下面是这个问题的改正方法:

RatPoly.appendTerm(StringBuffer sb, RatTerm rt)中加入下列代码:

                   if(sb.toString().equals("NaN"))



                     {



                     return;



                     }



                     



                     if(c.isNaN())



                     {



                     sb.replace(0, sb.length(), "NaN");



                     return;



                     }




将以上代码加入方法开始处以下代码之后:

                     RatNum c = rt.coeff;



                     int e = rt.expt;


问题2中错误提到“测试用例成功与否很大程度依赖于RatPoly.unparse()方法;如果大部分的测试都无法通过,你所编写的unparse()实现可能存在错误。”而我们已经给出了unparse()方法的实现。

问题&回答

这部分将对练习中一些常见问题进行澄清和回答。我们将尽力保持对这部分的更新,因此,当你遇到问题时这部分应该是你最先查看的地方(在仔细的阅读问题描述讲义和规格说明之后)。
 

问题:当isNaN()为真时,RatPoly.eval()应该返回什么样的RatPoly?

回答:java.lang.Double类中包含一个称为Double.NaN的静态final double类型用来表示NaN。在此情况下你应该返回它。

问题:RatPoly(0, n) 是否应该创建一个“0”多项式?我应该怎样实现“0”多项式?是否应该以一个系数为0的项来表示它?

回答:是的,RatPoly(0,n)应该创建一个“0”多项式。但是你不能用一个系数为0的项来表示“0”多项式,因为那不符合RatPoly的rep. invariant。你应该仔细的阅读RatPoly抽象函数中的最后一句话:“如果没有任何项,那么RatPoly表示一个零多项式”

问题:但是我还是不明白。rep. invariant中不是说项不能为空吗?那么怎么可能会没有任何项呢?

回答:是的,但是它仍能为空。请注意rep invariant中虽然说项自己不能为空,但是项所引用的RatTermVec对象却可以不包含任何项目。(这将导致terms.size()返回值为0。)对于这个问题,如果你仍有疑点,请向你的助教询问。(请阅读RatPoly中关于抽象函数和rep. invariant的代码注释。)

问题:为什么RatPoly构造器的coeff参数是一个int而不是RatNum?

回答:我们在测试用例中使用这个构造器。因为使用RatPoly.parse()是一个更有效的创建RatPoly对象的方式,我们就没有再提供别的以RatNum作为参数的构造器。

问题:我在使用Visio 2000时遇到了以下问题,在Visio 2000启动时弹出了一个错误提示对话框说"The procedure entry point GBL could not be located in the dynamic link library VISLIB32.DLL."我使用的是Windows2000系统。

回答:查阅微软的技术支持:Visio2000: Error Message: The Procedure Entry Point GBL Could Not Be Located in the Dynamic Link Library Vislib32.dll。

问题:我能否使用我自己的计算机(不是 Athena)来完成练习2?

回答:可以,但是你要从6.170课程下载相应的jar到你自己的机器上。对于练习2你需要从 /mit/6.170/lib目录下载junit.jar和ex2-lib.jar。但是你的代码必须能够在Athena上运行,所以在提交之前请先花一点时间来确认它们是否能够在Athena上面正确运行。

问题:在哪里能够找到JUnit API?

回答:你可以在这里找到JUnit API。




 
MIT Home
Massachusetts Institute of Technology Terms of Use Privacy