lab2回顾——P1(Poetic Walks)

lab2的主题是Abstract Data Type (ADT) and Object-Oriented Programming (OOP)

写在开头

抽象数据类型(ADT)和面向对象编程(OOP)是软件构造中最为重要的概念之一。在实践中,它们被广泛应用于各种程序设计任务,以期提高代码的可重用性、可维护性和可扩展性。

ADT 是一种“自我包容”的数据类型,它通过对数据和操作的抽象来实现对程序的组织和管理。与具体的实现方式无关,ADT的定义只关注它所扮演的角色以及其所需提供的接口,从而使程序的不同部分能够协同工作,并达到预期的目的。ADT 的优点在于,它可以使程序逻辑更加清晰、简洁、易于理解和修改,同时也增强了代码的可重用性。

然而,ADT 仍然缺乏一些关键的特性和功能,例如封装、多态和继承等,这些特性可以通过面向对象编程的方式来实现。面向对象编程是一种基于类和对象思想的编程范式,它使我们可以将 ADT 的思想进一步扩展到程序中,同时还提供了更为灵活、复杂的代码组织方式,以增强程序的可扩展性和可维护性。

在 OOP 中,ADT 被视为一个类,它包含了属性和方法来描述其数据和操作。类是一个模板,它定义了对象所具有的共同特征和行为,而对象则是类的一个实例化。针对同一个类,可以创建许多个不同的对象,它们之间可以相互交互、调用方法,并共享类所定义的属性和方法。

OOP 的好处在于,它提供了更加灵活和可扩展的代码组织方式,将程序设计中的复杂度分解到了更小的单元中,从而使得程序实现过程更为直观和自然,同时也增强了代码的可重用性和可维护性。除此之外,OOP 还支持多态和继承等特性,这些特性可以充分发挥 ADT 的优点,同时还有助于进一步减少代码的冗余,增强代码的可读性和可维护性。

总之,ADT 和 OOP 都是软件构造中非常重要的概念,两者可以相辅相成,在程序设计中起到至关重要的作用。通过对 ADT 和 OOP 的深入理解和实践,我们可以更好地理解程序的运作原理,并在编写代码时更为高效、安全和易于维护。

实验要求理解

Poetic Walks 是 MIT 6.031 课程中的一道实验,它是一个基于图数据结构和文学创作的小项目。该实验旨在让我们通过编写代码,将现有的诗歌作为输入数据,然后生成新的诗歌。生成过程采用了古典诗歌的格式和风格,同时也涉及到了图论算法的应用。

在实验中,首先需要根据诗歌中的韵律、节奏等特征,将每个单词转化成图中的节点,并建立相邻节点之间的边,形成一个语言模型。

最后,利用图论算法(例如深度优先搜索、广度优先搜索等),在建立好的语言模型上进行“漫步”,不断地生成新的句子和诗行,直至达到指定数量或达到预设的目标。通过生成的新诗,可以发现与原有诗歌的共性和差异,从而反映出代码实现的原理和功能。

总的来说,Poetic Walks 实验结合了文学和计算机科学的两个领域,利用数据结构和算法技术进行文学创作,既有趣味性又具备实用性。通过完成这个实验,我们可以深入了解图论算法和语言建模等方面的知识,同时也增强了对代码组织和规范化的理解。

实验环境

本次实验延续上一次的配置,未发现任何配置上的问题
环境:IntelliJ IDEA Community Edition 2021.1 x64、JDK8。
Libs(用到的第三方库):junit-4.12.jar、hamcrest-core-1.3.jar。
使用Git新建和导入仓库的操作与lab1的类似,即clone所需文件,建立Git仓库。
本次实验需要统计JUnit测试用例的代码覆盖度,在IntelliJ IDEA中已内置此工具。
在这里插入图片描述

任务要求

P1大致可以分为5个小任务:
①为Graph编写测试用例,需要划分等价类,实现良好的测试覆盖率。
②构造 Graph 接口来实现 spec 中的功能,并以 ConcreteEdgesGraph 和 ConcreteVerticesGraph 两种方式实现接口。
③将 ADT 泛型化。
④根据文件中的输入,使用③中的ADT构造 poet(有向图),最后实现生成 poem。
⑤对代码进行覆盖率测试。

具体实现

编写测试用例Test

Graph接口的静态构造方法:

/**
	 * Create an empty graph.
	 *
	 * @param <L> type of vertex labels in the graph, must be immutable
	 * @return a new empty weighted directed graph
	 */
	public static <L> Graph<L> empty() {
		return new ConcreteEdgesGraph<L>();
	}

GraphStaticTest.java的运行结果:
在这里插入图片描述
GraphInstanceTest.java:
对add()、set()、remove()、vertices()、sources()、targets()等方法写测试策略

实现ConcreteEdgesGraph

ConcreteEdgesGraph 类

分别写出Abstraction function、AF(vertices, edges)、Representation invariant和Safety from rep exposure。
例如Abstraction function需要指明私有变量vertices、edges的含义——
vertices:点的集合
edges:边的列表
它们实现了点与有向边组成有向图的抽象接口。

Edge类

同理如上。
其中AF(source, target, weight) = From the source to the target’s edge, whose value is the weight。

测试策略

测试方法分别有
testConcreteEdgesGraphToString(),testEdge(),testGetSource(),testGetTarget(),testGetWeight(),testToString(),testEquals(),testHashCode()

实现ConcreteVerticesGraph

同理如上。
其中ConcreteVerticesGraph 类涉及到的方法有:

public Vertex(String name);
private void checkRep();
public String getName();
public Map<String, Integer> getSources();
public Map<String, Integer> getTargets();
public int setSource(String vertex, int weight);
public int setTarget(String vertex, int weight);
public int setWeight(String vertex, int weight, Map<String, Integer> temp);
public String toString();
public boolean equals(Object other);
public int hashCode();

测试类中涉及的方法有:

testGraghToString();
testVertex();
testGetSource();
testGetTarget();
testSetSource();
testSetTarget();
testToString();
testEquals();
testHashCode();

测试结果如图:
在这里插入图片描述

设计诗歌 Poetic walks

Poetic walks 是一个 MIT 实验室的项目,旨在将人类诗歌与计算机科学相结合,从而生成新的、具有艺术性的文本。
首先需要处理诗歌的数据,包括从现有的文本中提取单词、句子以及诗歌的格式等信息。
对于每个单词,需要将其转换为带权有向图中的一个点,以便在计算机上进行操作和比较。
最终目标是将人类诗歌和计算机科学相结合,从而创造出新的具有艺术性的文本。
思路如下:

public class GraphPoet {
	private final Graph<String> graph = Graph.empty();
	/**
	 * 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 br = new BufferedReader(new FileReader(corpus));
		//打开输入流,缓存读入数据
		String read;
		while ((read = br.readLine()) != null) {
			//将read保存至集合
			String[] word = read.split("\\s");
			for (int i = 0; i < word.length - 1; i++) {
				int weight = graph.set(word[i].toLowerCase(), word[i + 1].toLowerCase(), 1);
				if (weight >= 1) {
					graph.set(word[i].toLowerCase(), word[i + 1].toLowerCase(), weight + 1);
				}
			}
		}
		br.close();
		checkRep();
	}
	/*
	 checkRep
	 检查不变性:
	 1.graph内所有点非空
	 2.graph内所有边的weight不为0(遍历检查vertex的sources边)
	*/
	private void checkRep() {
		for (String vertex : graph.vertices()) {
			if (vertex == null) {
				throw new IllegalArgumentException("Vertex cannot be null");
			}
			if (vertex.isEmpty()) {
				throw new IllegalArgumentException("Vertex cannot be empty");
			}
			Map<String, Integer> sources = graph.sources(vertex);
			for (Map.Entry<String, Integer> entry : sources.entrySet()) {
				if (entry.getValue() < 0) {
					throw new IllegalArgumentException("Source weight cannot be negative");
				}
			}
		}
	}
	/**
	 * Generate a poem.
	 *
	 * @param input string from which to create the poem
	 * @return poem (as described above)
	 */
	public String poem(String input) {
		if (input == null) {
			throw new IllegalArgumentException("The input is empty!");
		}
		if (input.equals("")) return "";
		String[] word = input.split("\\s");
		String poem = word[0] + " ";
		for (int i = 0; i < word.length - 1; i++) {
			if (graph.vertices().contains(word[i + 1].toLowerCase())) {
				Map<String, Integer> sources = graph.sources(word[i + 1].toLowerCase());
				Map<String, Integer> targets = graph.targets(word[i].toLowerCase());
				String max = null;
				int maxValue = 0;
				for (String source : sources.keySet()) {
					if (targets.containsKey(source)) {
						if (sources.get(source) + targets.get(source) > maxValue) {
							max = source;
							maxValue = sources.get(source) + targets.get(source);
						}
					}
				}
				if (max != null)
					poem = poem.concat(max + " ");
			}
			poem = poem.concat(word[i + 1] + " ");
		}
		checkRep();
		return poem;
	}
	/**
	 *  toString()
	 *  @return 以字符串形式返回poet的结构
	*/
	@Override
	public String toString() {
		checkRep();
		return graph.toString();
	}
}

总结

好不容易终于做完了。

首先在看MIT网站上的实验要求描述就受到了很大震撼——英文原文看不懂,翻译过来又因为文化差异的缘故,根本看不懂是什么意思!!!╥﹏╥导致我理解上花了大量时间。后来几次软构课经过王老师将课上知识与实验二要求结合起来讲,我才渐渐理解了任务一的要求——利用泛型化实现对vertex、edge等各个类的ADT。慢慢的,我也理解到泛型化的巨大力量和作用——它不但简化了工作量和代码量,也使得程序的主次层次更为分明。

总结起来,在总时间的占用中,主要的部分并不是代码的编写和实现,而是ADT的设计和测试用例的编写。这也使我更能专注于从实现功能的角度去完成整个项目。Spec的编写和测试用例的编写是最让我感到头痛的,因为这不同于写代码,要设计一个合格的ADT需要考虑许多内容。

在实验的完成中我也收获了许多教训和经验。首先,我得时刻提醒自己不能总是在先完成主程序的编写后再完成测试用例,否则这样会花费更多的时间,要时刻遵循“测试优先”的原则。我还是比较满意于自己对ADT的设计的。但这也只是开始,希望接下来能在lab3中有更好的表现。给自己加油!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值