Lab2相比Lab1就考的不是提交啥的了,主要是在考规约,注释,写具有独立性的代码,写测试文件,泛型这些构造软件所需要的要素。我们这届Lab2就两部分,P2与Lab1有重合,而且内容也不多,所以主要说一下P1。
1测试文件
Lab2是让你写一个有向图,需要一个构造的边的类ConcreteEdgesGraph和一个点的类ConcreteVerticesGraph,所以对这两个类分别写测试ConcreteEdgesGraphTest和ConcreteVerticesGraphTest,公共的测试写在GraphInstanceTest中,内两个测试都会调用这个测试,最后选用构造的点类和边类的其中一个,按这个构造出有向图,并进行最后的测试GraphStaticTest。具体每个测试中都要写什么,在Graph.java文件的规约中都已经写好了,按照Graph中给出的函数,一个一个写测试代码就好。
代码比较长,而且每个人测试的重点也不一样,所以就不给出测试代码了。
2.类ConcreteEdgesGraph和类ConcreteVerticesGraph
第二部分是这个实验的核心,拿类ConcreteEdgesGraph来说,java文件中需要自己写的就两部分。第一,填写类ConcreteEdgesGraph,缺少的代码就是Graph中让你写内一堆,要求都在规约里,就按照你写的内个测试文件写对应的功能代码就行。唯独没给规约的就一个ToString函数,就一个输出,写一下就行。第二部分是写类Edge,需要满足独立性,所以对于私人域就要用函数来找。考虑一下要求的是mutable还是inmutable,不可变就复制再用,可变就直接改。对于类ConcreteVerticesGraph也是一样的,就不赘述了。下面是代码:
ConcreteEdgesGraph:
/* Copyright (c) 2015-2016 MIT 6.005 course staff, all rights reserved.
* Redistribution of original or derived work requires permission of course staff.
*/
package P1.graph;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* An implementation of Graph.
*
* <p>PS2 instructions: you MUST use the provided rep.
*/
public class ConcreteEdgesGraph<L> implements Graph<L> {
private final Set<L> vertices = new HashSet<>();
private final List<Edge<L>> edges = new ArrayList<>();
// Abstraction function:
// TODO
//a graph with weight direct edges,every edge is begin from source and end to
//target,source and target must be different vertex
// Representation invariant:
// TODO
//the weight must be positive,from a source to a target is only one edge
// Safety from rep exposure:
// TODO
// all of the fields are private and final,because immutable,so they can't be
//accessed from outside the class or be given a new value,if you want to use
//them ,just do some defensive copying in the return.
// TODO constructor
public ConcreteEdgesGraph() {
vertices.clear();
edges.clear();
checkRep();
}
public ConcreteEdgesGraph(final List<L> vertices) {
this.vertices.addAll(vertices);
checkRep();
}
// TODO checkRep
private void checkRep() {
for(L vertex : vertices) {
assert vertex != null;
}
}
@Override public boolean add(L vertex) {
if (this.vertices.contains(vertex)) return false;
vertices.add(vertex);
return true;
}
@Override public int set(L source, L target, int weight) {
//规约里没说小于0的情况,直接抛异常。
if(weight < 0) throw new RuntimeException("Error, input a negative number");
//大于0时,需要先检查在不在边集和点集中,不在就加进去。等于0,不在正好,在还要删掉(实现删掉的代码在下面)。
if (weight > 0) {
add(source);
add(target);
}
//开始更新边
Edge<L> tempEdge = new Edge<>(source,target,weight);
//如果有一模一样的边,则啥也不改,返回权重
if(edges.contains(tempEdge)) return tempEdge.getWeight();
//否则更新边或者删除
for(Edge<L> temp : edges) {
if((temp.getSource().equals(source))&&(temp.getTarget().equals(target))) {
if(weight == 0) {//当weight是0时,移除对应边
edges.remove(temp);
return temp.getWeight();
}else {//weight不是0时,修改对应边
edges.remove(temp);
edges.add(tempEdge);
return temp.getWeight();
}
}
}
//如果不是已有边就新建
if(weight != 0) edges.add(tempEdge);//weight就直接跳过了
return 0;
}
@Override public boolean remove(L vertex) {
// boolean flag = false;
// if(vertices.contains(vertex)) {
// for(String temp1 : vertices) {
// if(temp1.equals(vertex)) {
// vertices.remove(temp1);
// flag = true;
// break;
// }
// }
// if(flag)
// {
// for(Edge temp2 : edges) {
// if(temp2.getSource().equals(vertex) || temp2.getTarget().equals(vertex)) {
// edges.remove(temp2);
// }
// }
// }
//
// }
// return flag;
Iterator<L> iterator1 = vertices.iterator();
boolean flag = false;
while (iterator1.hasNext()) {
L string = iterator1.next();
if (string.equals(vertex)) {
flag = true;
iterator1.remove();
break;
}
}
Iterator<Edge<L>> iterator2 = edges.iterator();
while (iterator2.hasNext()) {
Edge<L> edge = iterator2.next();
if (edge.getSource().equals(vertex) || edge.getTarget().equals(vertex)) {
iterator2.remove();
}
}
checkRep();
return flag;
}
@Override public Set<L> vertices() {
return new HashSet<L>(vertices);
}
@Override public Map<L, Integer> sources(L target) {
HashMap<L, Integer> sourceMap = new HashMap<L , Integer>();
for(Edge<L> temp : edges) {
if(temp.getTarget().equals(target)) {
sourceMap.put(temp.getSource(),temp.getWeight());
}
}
return sourceMap;
}
@Override public Map<L, Integer> targets(L source) {
HashMap<L, Integer> targetMap = new HashMap<L, Integer>();
for(Edge<L> temp : edges) {
if(temp.getSource().equals(source)) {
targetMap.put(temp.getTarget(),temp.getWeight());
}
}
return targetMap;
}
// TODO toString()
@Override
public String toString()
{
System.out.println("vertices:");
for(L temp1 : vertices) {
System.out.print(temp1);
}
System.out.println("edges:");
for(Edge<L> temp2 : edges) {
System.out.print(temp2);
}
return "vertice number: " + vertices.size() + " edge number: " + edges.size();
}
}
/**
* TODO specification
* Immutable.
* This class is internal to the rep of ConcreteEdgesGraph.
*
* <p>PS2 instructions: the specification and implementation of this class is
* up to you.
*/
class Edge<L> {
// TODO fields
private final L source;
private final L target;
private final int weight;
// Abstraction function:
// TODO
//an edge class,have source,target and weight
// Representation invariant:
// TODO
//source and target are different,and they can't be NULL
//weight must be nonnegative
// Safety from rep exposure:
// TODO
// all of the fields are private and final,because immutable,so they can't be
//accessed from outside the class or be given a new value,if you want to use
//them ,just do some defensive copying in the return.
// TODO constructor
//建立新边的时候就直接给定起点终点和权重。
public Edge(L source, L target, int weight)
{
this.source = source;
this.target = target;
this.weight = weight;
checkRep();
}
// TODO checkRep
public void checkRep() {
assert weight >= 0;
assert source != null;
assert target != null;
}
// TODO methods
/**
* @return the source vertex of the directed edge
*/
public L getSource() {
L tempsource = source;
return tempsource;
}
/**
* @return the target vertex of the directed edge
*/
public L getTarget() {
L temptarget = target;
return temptarget;
}
/**
* @return the weight of the directed edge
*/
public int getWeight() {
int tempweight = weight;
return tempweight;
}
// TODO toString()
@Override
public String toString() {
return this.source + " -> " + this.target + ": " + this.weight + "\n";
}
}
ConcreteVerticesGraph:
/* Copyright (c) 2015-2016 MIT 6.005 course staff, all rights reserved.
* Redistribution of original or derived work requires permission of course staff.
*/
package P1.graph;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import P1.graph.Vertex;
/**
* An implementation of Graph.
*
* <p>PS2 instructions: you MUST use the provided rep.
*/
public class ConcreteVerticesGraph<L> implements Graph<L> {
private final List<Vertex<L>> vertices = new ArrayList<>();
// Abstraction function:
// TODO
//a graph with weight direct edges,every edge is begin from source and end to
//target,source and target must be different vertex
// Representation invariant:
// TODO
//the weight must be positive,every vertex must be different
// Safety from rep exposure:
// TODO
// all of the fields are private,and mutable.
// TODO constructor
public ConcreteVerticesGraph() {
vertices.clear();
checkRep();
}
// TODO checkRep
private void checkRep(){
final int size = vertices.size();
if(!vertices.isEmpty()){
for(int i = 0; i < size; i++) {
for(int j = i + 1; j < size; j++){
assert !vertices.get(i).equals(vertices.get(j));
}
}
}
}
@Override public boolean add(L vertex) {
//建立vertices对应的Set
Set<L> vertexSet = vertices();
Vertex<L> tempVertex = new Vertex<L>(vertex);//给vertex新建一个点,如果没有就加进去
if(!vertexSet.contains(vertex)){
vertices.add(tempVertex);
return true;
}
else return false;
}
@Override public int set(L source,L target, int weight) {
//规约里没说小于0的情况,直接抛异常。
if(weight < 0) throw new RuntimeException("Error, input a negative number");
//建立vertices对应的Set
Set<L> vertexSet = vertices();
Vertex<L> tempSource = findVertex(source);
Vertex<L> tempTarget = findVertex(target);
//等于0,不在正好,如果点不在vertices里就可以直接return了。在的情况需要返回边长,下面再写。
if(weight == 0) {
if(!vertexSet.contains(source)||(!vertexSet.contains(target)))
return 0;
}
//大于0时,需要先检查在不在边集和点集中,不在就加进去。
if(weight > 0) {
add(source);
add(target);
}
if(tempSource.getTargetEdges().containsKey(target)) {//如果有边
//准备好旧的权重,用于返回
int oldWeight = tempSource.getTargetEdges().get(target);
//如果weight不等于0,更新值 如果weight等于0,删除该边
if(weight != 0) {
tempSource.getTargetEdges().remove(target);
tempSource.getTargetEdges().put(target,weight);
tempTarget.getSourceEdges().remove(source);
tempTarget.getSourceEdges().put(source,weight);
}else {
tempSource.getTargetEdges().remove(target);
tempTarget.getSourceEdges().remove(source);
}
return oldWeight;
}else {
//如果没边如要是等于0,删除不用做因为没有边,直接返回weight也就是0
if(weight != 0) {//weight不等于0,在vertices加入边再返回0,
tempSource.getTargetEdges().put(target, weight);
tempTarget.getSourceEdges().put(source, weight);
}//weight等于0就直接跳过了
return 0;
}
}
@Override public boolean remove(L vertex) {
//建立vertices对应的Set
Set<L> vertexSet = vertices();
boolean flag = false;
if(vertexSet.contains(vertex)) {
Vertex<L> tempVertex = findVertex(vertex);
//建立以这个点为起点和重点的set
Set<L> sourceEdgeSet = tempVertex.getSourceEdges().keySet();
Set<L> targetEdgeSet = tempVertex.getTargetEdges().keySet();
//更新所有以目标点为终点的点,在他们的getTargetEdges()集合中去除目标点
for(L temp1 : sourceEdgeSet) {
Vertex<L> temp1vertex = findVertex(temp1);
temp1vertex.getTargetEdges().remove(vertex);
}
//更新所有以目标点为起点的点,在他们的getSourceEdges()集合中去除目标点
for(L temp2 : targetEdgeSet) {
Vertex<L> temp2vertex = findVertex(temp2);
temp2vertex.getSourceEdges().remove(vertex);
}
//删除顶点
vertices.remove(tempVertex);
flag = true;
}
return flag;
}
@Override public Set<L> vertices() {
Set<L> strSet = new HashSet<>();
for(Vertex<L> vt : vertices) {
strSet.add(vt.getVertex());
}
return strSet;
}
@Override public Map<L, Integer> sources(L target) {
Vertex<L> tempVertex = findVertex(target);
return tempVertex.getSourceEdges();
}
@Override public Map<L, Integer> targets(L source) {
Vertex<L> tempVertex = findVertex(source);
return tempVertex.getTargetEdges();
}
/**
* Find the Vertex from vertices
*
* @param the vertex of Vertex
* @return the Vertex
* @throw if can't find,throw new RuntimeException("vertex : no find \n");
*/
public Vertex<L> findVertex(L vertex) {
for(Vertex<L> temp : vertices) {
if(temp.getVertex().equals(vertex)){
Vertex<L> tempVertex = temp;
return tempVertex;
}
}
Vertex<L> tempVertex = new Vertex<L>(vertex);
vertices.add(tempVertex);
return tempVertex;
}
// TODO toString()
@Override
public String toString() {
return "The vertex number : "+ vertices.size() ;
}
}
/**
* TODO specification
* Mutable.
* This class is internal to the rep of ConcreteVerticesGraph.
*
* <p>PS2 instructions: the specification and implementation of this class is
* up to you.
*/
class Vertex<L> {
// TODO fields
private L vertex;
private Map<L, Integer> sourceEdges = new HashMap<>();
private Map<L, Integer> targetEdges = new HashMap<>();
// Abstraction function:
// TODO
// Representation invariant:
// TODO
// Safety from rep exposure:
// TODO
// TODO constructor
/**
* Create a vertex with its name
*
* @param name
* the identifier of every vertex.
*/
//建立新点的时候就直接给定vertex
public Vertex(L vertex)
{
this.vertex = vertex;
checkRep();
}
// TODO checkRep
private void checkRep() {
assert vertex != null;
}
// TODO methods
/**
* @return the label of this vertex.
*/
public L getVertex() {
return vertex;
}
/**
* @return the source Edges of this vertex.
*/
public Map<L, Integer> getSourceEdges() {
return sourceEdges;
}
/**
* @return the target Edges of this vertex.
*/
public Map<L, Integer> getTargetEdges() {
return targetEdges;
}
// TODO toString()
@Override
public String toString() {
return "vertex : " + vertex;
}
}
3.泛型
其实跟第二部分没有区别,把2中的String全改成L就完事了。(由于最后交的就是L,所以第二部分的代码就是用L写的。
4.诗人的漫步
最好玩的一部分,你需要自己写一个诗,来检验你的代码。就是以之前你写的内个有向图为基础,根据文本诗歌来构造这个有向图,要求和例子看英文网页吧,最后看一下你这个有向图能不能按照人家要求的功能来。代码如下:
/* Copyright (c) 2015-2016 MIT 6.005 course staff, all rights reserved.
* Redistribution of original or derived work requires permission of course staff.
*/
package P1.poet;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
//import java.util.Set;
import P1.graph.Graph;
/**
* A graph-based poetry generator.
*
* <p>GraphPoet is initialized with a corpus of text, which it uses to derive a
* word affinity graph.
* Vertices in the graph are words. Words are defined as non-empty
* case-insensitive strings of non-space non-newline characters. They are
* delimited in the corpus by spaces, newlines, or the ends of the file.
* Edges in the graph count adjacencies: the number of times "w1" is followed by
* "w2" in the corpus is the weight of the edge from w1 to w2.
*
* <p>For example, given this corpus:
* <pre> Hello, HELLO, hello, goodbye! </pre>
* <p>the graph would contain two edges:
* <ul><li> ("hello,") -> ("hello,") with weight 2
* <li> ("hello,") -> ("goodbye!") with weight 1 </ul>
* <p>where the vertices represent case-insensitive {@code "hello,"} and
* {@code "goodbye!"}.
*
* <p>Given an input string, GraphPoet generates a poem by attempting to
* insert a bridge word between every adjacent pair of words in the input.
* The bridge word between input words "w1" and "w2" will be some "b" such that
* w1 -> b -> w2 is a two-edge-long path with maximum-weight weight among all
* the two-edge-long paths from w1 to w2 in the affinity graph.
* If there are no such paths, no bridge word is inserted.
* In the output poem, input words retain their original case, while bridge
* words are lower case. The whitespace between every word in the poem is a
* single space.
*
* <p>For example, given this corpus:
* <pre> This is a test of the Mugar Omni Theater sound system. </pre>
* <p>on this input:
* <pre> Test the system. </pre>
* <p>the output poem would be:
* <pre> Test of the system. </pre>
*
* <p>PS2 instructions: this is a required ADT class, and you MUST NOT weaken
* the required specifications. However, you MAY strengthen the specifications
* and you MAY add additional methods.
* You MUST use Graph in your rep, but otherwise the implementation of this
* class is up to you.
*/
public class GraphPoet {
private final Graph<String> graph = Graph.empty();
// Abstraction function:
// TODO
//build graph poet
// Representation invariant:
// TODO
//bridge都是小写
// Safety from rep exposure:
// TODO
//空格,空行
/**
* Create a new poet with the graph from corpus (as described above).
*
* @param corpus text file from which to derive the poet's affinity graph
* @throws IOException if the corpus file cannot be found or read
*/
public GraphPoet(File corpus) throws IOException {
//读入文件
BufferedReader bf = null;
bf = new BufferedReader(new FileReader(corpus));
List<String> poemList = new ArrayList<>();
String templine;
while ((templine = bf.readLine()) != null) {
String[] strArray = templine.split(" ");
for(int i = 0; i < strArray.length; i++)
{
poemList.add(strArray[i]);
}
}
for (int i = 0; i < poemList.size() - 1; i++) {
int last = 0;
String s = poemList.get(i).toLowerCase();
String t = poemList.get(i+1).toLowerCase();
graph.add(s);
graph.add(t);
last = graph.set(s, t, 1);
graph.set(s, t, last+1);
}
bf.close();
checkRep();
}
// TODO checkRep
private void checkRep() {
for (String vertex : graph.vertices()) {
int count = 0;
while (count < vertex.length()) {
assert (!Character.isUpperCase(count));
count++;
}
assert (!vertex.equals(""));
assert (!vertex.equals(" "));
assert (!vertex.equals("\n"));
}
}
/**
* Generate a poem.
*
* @param input string from which to create the poem
* @return poem (as described above)
*/
public String poem(String input) {
StringBuilder stbr = new StringBuilder();
String[] strArray = input.split(" ");
List<String> wordList = new ArrayList<String>(Arrays.asList(strArray));
Map<String, Integer> sourceEdges;
Map<String, Integer> targetEdges;
for (int i = 0; i < wordList.size() - 1; i++) {
String s = wordList.get(i).toLowerCase();
String t = wordList.get(i + 1).toLowerCase();
stbr.append(wordList.get(i)).append(" ");
targetEdges = graph.targets(s);
sourceEdges = graph.sources(t);
int max = 0;
String bridgeWord = "";
for (String string : targetEdges.keySet()) {
if (sourceEdges.containsKey(string)) {
if (sourceEdges.get(string) + targetEdges.get(string) > max) {
max = sourceEdges.get(string) + targetEdges.get(string);
bridgeWord = string;
}
}
}
if (max > 0) {
stbr.append(bridgeWord + " ");
}
}
stbr.append(wordList.get(wordList.size() - 1));
return stbr.toString();
}
// TODO toString()
@Override
public String toString() {
return graph.toString();
}
}
/* Copyright (c) 2015-2016 MIT 6.005 course staff, all rights reserved.
* Redistribution of original or derived work requires permission of course staff.
*/
package P1.poet;
import java.io.File;
import java.io.IOException;
/**
* Example program using GraphPoet.
*
* <p>PS2 instructions: you are free to change this example class.
*/
public class Main {
/**
* Generate example poetry.
*
* @param args unused
* @throws IOException if a poet corpus file cannot be found or read
*/
public static void main(String[] args) throws IOException {
final GraphPoet nimoy = new GraphPoet(new File("src/P1/poet/mugar-omni-theater.txt"));
final String input = "Test the system.";
System.out.println(input + "\n>>>\n" + nimoy.poem(input));
System.out.println("*****This is something interesting*****");
//Graph poerty salm input short and output a whole poem
final GraphPoet graphPoet = new GraphPoet(new File("src/P1/poet/mypoem.txt"));
interesting(graphPoet,"Are Fair");
interesting(graphPoet,"Parsley thyme");
interesting(graphPoet,"Remember there");
interesting(graphPoet,"He mine");
System.out.print("\n");
interesting(graphPoet,"Tell shirt");
interesting(graphPoet,"Parsley thyme");
interesting(graphPoet,"Without needlework");
interesting(graphPoet,"Then mine");
System.out.print("\n");
interesting(graphPoet,"Tell land");
interesting(graphPoet,"Parsley thyme");
interesting(graphPoet,"Between strands");
interesting(graphPoet,"Then mine");
System.out.print("\n");
}
public static void interesting(GraphPoet graphPoet,String input)
{
String output = input;
for(int i = 0; i < 3; i++ )
{
output = graphPoet.poem(output);
}
System.out.println(output);
}
}