oo第一次博客
第一次作业,正则表达式
本周我们迎来第一项任务,java面向对象之实现对一串字符的匹配以及构造出计算的多项式,最终得出计算的结果。简而言之,可以用以下的要求来看题目:
- 输入的多项式字符串中不得出现非法字符
- 多项式的输入型式,{多项式 1}+{多项 式 2}-…… +{多项式 n},多项式内部格式为{(c1,n1),(c2,n2),…,(cm,nm)}
- 其中多项式的输入个数不得超过20个,同时多项式的内部数据的对数不会超过50对
- 输入中的所有空格应该被过滤掉
- 在本次编程过中, 对 于数(c,n),c为系数, 有-10^6<c<10^6(即 c前面可以 有‘ -’) , n为幂, 有 0<=n<10^6,即 c最大为 6位十进制整数, 位十进制整数, n最大为 6位十进 制整数。所有均可能出现前导 制整数。所有均可能出现前导 0,但 c和 n除去符号位之外的长度分别不超过 6和 6。
- 对于所有的不合法的输入,程序要给出响应,即输出为“ERROR”
那我们社会人应该怎么解决这道题目呢?
如果按照面向流程的思路设计,我们以c语言为例,首先是判断输入是否合法,采用的方法是对输入进行输入流判断,然后就是一路遍历,取出合法的多项式,然后构成多项式,最后进行计算,而作为初入面向对象的小白来说,第一次作业就用面向对象来写,着实有些困难,所以在下还是用一个C语言的形式来实现这个作业了,那么我是具体如何实现的呢?
首先是正则表达式:对于转换可以大家可以自行搜索,主要讲一下正则表达式的匹配的吧,对于该问题,我是这样考虑的,从整体的多项式的结构来看,我们分析其中的字符特点,有以下几个特点:
- 对于输入字符串的第一个和最后一个字符,只能是‘{’和‘}‘,当然这里是过滤了开头的字符,因为那本身并没有太大意义
- 统计输入字符串,对于合法的输入,‘{’的个数一定等于‘}’的个数,一定等于“\\{(\\([+-]?[0-9]{1,6},(([+]?[0-9]{1,6})|([-]?[0]{1,6}))\\),){0,49}\\([+-]?[0-9]{1,6},(([+]?[0-9]{1,6})|([-]?[0]{1,6}))\\)\\}”的个数,且一定比”\\}[+-]\\{“多一个,所以我们采用统计这几类正则表达式的个数来判断输入字符串是否合法
- 在统计的过程中对输入的个数进行判断,
- 最后我们提取出每一个多项式,并在多项式中提取出数字
- 根据之前我们在分段时候采用的”\\}[+-]\\{”,将计算符号记录下来,最后进行计算,
关于正则匹配的代码
public static void find(String All_2, String str_num) {
Pattern p_2 = Pattern.compile(str_num);
Matcher m_2 = p_2.matcher(All_2);
while(m_2.find() && result4==1) {
formula(big, m_2.group(), no_5);
big++;
}
}
这里ALL_2,表示输入的字符串,str_num表示多项式的正则表达式,find()函数就是找出符合条件的多项式,在find()内部调用formula的作用是分析多项式,取出数字,为了计算多项式的个数我们采用了m.find()方法,而不是matches(),是正则表达式的完全匹配,而find()是包含匹配,所以能过找到多个不同的符合条件的式子,其实对于这一过程使用split也可以达到同样的效果。之后的计算以及输出过程就不做过多的叙述了。
oo第二次作业,傻瓜电梯
本周我们完成的任务是傻瓜电梯的调度,对于那十分十分详细的指导书,我感觉想要说明白题目要求,是做不到的,所以就把指导书贴出来给大家看了,,由于在下还不会网页制作,只能通过百度网盘了,https://pan.baidu.com/s/12KLXnQa9ifh1hZQrvqhjAw/pdf争取下次用网页吧,虽然不知道什么时候才能学会。
看完题目我们开始分析吧,
从最直接的角度分析就是请求与调度的关系,直接点就是,发出请求,然后处理请求,将所有合法请求过程构成请求队列,调度器处理一个请求就是根据当前电梯状态发出一条指令,然后电梯运行该条指令,然后更新电梯的状态,然后返回状态只调度器,直到执行完最后一条指令。所以按照思想构造出五个类,但这次作业,在下并没有完全按照要求写,虽然是五个类,但是五个类的作用并不是完全按给出的建议。首先说一下几个类的功能:
- 输入我们使用主类,虽然输入是来自电梯类与楼层类,但是作为两处输入太过于麻烦,所以我们使用主类输入
- W_legal类,该类作为对于输入的第一层判断,也就是简单的过滤一些不合法输入
- Demand类,该类也是作为对于输入的判断,将所有不合法的输入过滤,得到是正确的请求队列
- Scheduler类,该类就调度器类,在该类中判断是否是同质请求,同时时间与队列是该类的关键的两个属性,时间用于判断当前类的请求时间与当前时间的大小,请求队列用于判断是否执行结束
- Elevator类,该类就是电梯类,接收来自调度器的指令,然后执行该指令,该类中的方法change()意思就是执行指令,然后改变电梯的运行状态,然后返回状态给调度类
对于该想法,本来我也觉得天衣无缝,只可惜还是有漏网之鱼,先列出自己的bug吧:
- 对于输入,我依旧采用了第一次作业方法,对输入使用find(),错误……,导致只要存在符合条件的输入就会执行,而忽略了必须是完全匹配的条件
- 对于数字的判断,题目的要求是4字节的整数,由于在java中是不存在unsigned类型的数据类型的,也就意味着,我们的数字最大位2^32-1,而不是2^31-1,所有数据类型要是long类型,我都是这么写的,但是还是错了,那么错误在哪里呢?本来在使用下面的这个方法时要注意一点时,一定要是有try catch,除非你事先确定了数据的位数
-
max = Long.parseLong("4294967295");
对于自己想法的优化:
- 最需要优化的地方当然是输入,所以第三次作业在下重写写了一个输入,具体见第三次作业,
- 如果还有什么要优化的话那就是代码风格吧
为了纪念自己第一次使用面向对象的方法来实现这个作业,容我贴一点核心代码
request_new.request_old = legal_1.num;
request_new.deal(); //得到一个队列,队列中是按时间顺序的请求都是合法请求
sche.request_n = request_new.request1;
sche.fetch();
while(sche.request_n[sche.i][0]!=0) {
sche.state_c = elev.state;
sche.stop_time = elev.st_time;
sche.deal_t();
elev.move = sche.instr_o;
elev.change();
sche.i++;
}
oo第三次作业,电梯进化
在上一周完成的基础上,大家觉得这电梯是不是有点low啊,现在都智能化了,哪还有这么蠢的电梯啊,所以我们要让电梯的调度智能化,怎么个智能化呢?我们这里也只是简单的考虑以下捎带问题,所谓捎带,就是当我在电梯去往目的地的途中,按下一个可以在途中解决的按钮,那么这次请求就可以被捎带了,具体怎么说呢?主要以下说明:
- (e.sta = UP→10>=e.n>e.e_n) || (e.sta = DOWN1<=e.n<e.e_n)1 注释:本段所述的可捎带条件可以这样理解:①电梯状态向上运行时,发出向上请求的楼层处于当前楼层和10层之间;②电梯状态向下运行时,发出向下请求的楼层处于当前楼层和1层之间。
- 对于任意一个楼层请求r=(FR, n, dir, t),如果电梯当前是运动状态,则顺路捎带请求一定有: (r.dir=e.sta) && ((r.dir=UP(r.n <= e.n)&&(r.n>e.e_n)) || (r.dir=DOWN(r.n>=e.n)&&(r.n<e.e_n))) 注释:本段所述的可捎带条件可以这样理解:电梯状态向上或向下运行时,新楼层请求的运动方向与当前方向一致,而且新请求发生的楼层在当前所处楼层和目标楼层之间。
- 对于任意一个电梯内运行请求r=(ER,n,t),如果是电梯当前运动状态下的顺路捎带请求,则一定有: (e.sta=UP(10=>r.n > e.e_n)) || (e.sta=DOWN (1<=r.n<e.e_n)) 注释:本段所述的可捎带状态可以这样理解:电梯内请求的目标楼层在电梯的前进方向上
- 对于e.sta = STILL状态分为2种情况。一是在电梯处于停留状态(WFS状态),此种情况没有“顺路捎带”请求,因为此时请求队列为空;(注意STILL状态仅用于输出表达,输入只有UP/DOWN两个状态)
- 可捎带请求的条件是,该请求时非同质请求
当时我看的时一脸懵逼啊,可谓是一点思路都没有。现在才发现,当老师也不容易啊,写完了讲不清…………
待我慢慢道来,我们分为以下几步;
如何解决捎带问题
我们可以将捎带分为两类:
- 第一类,在电梯途中的电梯外的请求,该请求必须在电梯的当前楼层与目标楼层之间,且方向与电梯运动方向相同
- 第二类,电梯内的请求,这一类请求意味着只要请求在电梯当前楼层之外,并且方向与运动方向相同,都可以捎带
那么如何捎带了,相信大家到这时思路都差不多了,构建捎带队列,而捎带队列的构建,小编我是想了整整一晚上啊,硬是没开始写代码。当时构建的方法主要有两种想法
- 按照时间构建捎带队列,将请求队列中符合稍带条件的请求按时间直接装入捎带队列,执行时按楼层执行,这样就不可避免的在执行的时候需要遍历一遍捎带队列,找出最接近的楼层捎带,更多的细节在于捎带请求完成后需要清空这一条捎带请求,需要将捎带队列改变,又会构成麻烦
- 按照楼层直接存储捎带队列,由于捎带有一个特点,那就是捎带会沿着电梯前进方向执行,举例来说,从一楼到十楼的过程中,被捎带的请求一定是从中间楼层,且方向向上的请求,或者电梯内请求。按照这种方式存储电梯也会带来麻烦,那就是同一楼层不止一条请求,且同一楼层的请求要按照输入的顺序输出,也就意味这在楼层中的请求也需要排序,不过相对于第一种,这个时间就不算什么了,所以我们在调度器中构建三维数组表示捎带请求队列
这里给出上行途中电梯内请求的捎带的例子:
if(this.request_n[k][0] > this.station[0] && this.pr_instr[0]>this.station[0] && this.request_n[k][2]==2 && this.request_n[k][4]==0) {
for(floor=this.station[0];floor < this.request_n[k][0];floor++) {
if(this.on_way[floor][0][0]!=0) {
arrive++;
}
arrive+=0.5;
}
//计算出从当前楼层到队列中一层的时间
if(this.occupy[(int) this.request_n[k][0]][1]!=0 && (arrive+this.stop_time)>this.request_n[k][1]) //表示该层楼电梯内的按钮已经被占用
{
this.request_n[k][4] = 1;
System.out.printf("#SAME");
output2(this.request_n[k]);
}
/*import*/ else if((arrive+this.stop_time) > this.request_n[k][1] && this.request_n[k][4]==0) { //判断时间问题,运行时有没有已经到达这一层
chose = 0;
while(this.on_way[(int) this.request_n[k][0]][chose][0]!=0) { //表示按钮还没有被按,可以捎带,不是同质请求
chose++;
}
if(chose==1){ //如果改楼层有多条请求被捎带,按照输入顺序排好顺序
if((k+1)<this.on_way[(int) this.request_n[k][0]][0][4])
{
temp = this.on_way[(int) this.request_n[k][0]][0];
this.on_way[(int) this.request_n[k][0]][0] = this.request_n[k];
this.on_way[(int) this.request_n[k][0]][chose] = temp;
}
else {
this.on_way[(int) this.request_n[k][0]][chose] = this.request_n[k];//表示向上运行的电梯内请求捎带
}
}
else{
this.on_way[(int) this.request_n[k][0]][chose] = this.request_n[k];//表示向上运行的电梯内请求捎带
}
if(this.occupy[(int) this.request_n[k][0]][2]==0 || this.occupy[(int) this.request_n[k][0]][3]==0) {
j++;
}
this.occupy[(int) this.request_n[k][0]][1]=1; //表示电梯内的按钮被按下
this.request_n[k][4] = k+1; //表示该指令放入捎带队列中将被执行
}
}
主请求起到关键的作用
主请求作为该次电梯执行的目标楼层,就是运行的最后一层,决定了电梯的运行方向,以及会捎带那些请求,在我们采用第二种方法后,每次执行的时整个请求队列,也就意味着,我们每次都将主请求执行结束,才意味着一次执行的结束,所以每次执行后都需要更新主请求,更新的主请求有两个来源,捎带队列中还未执行的请求,请求队列中还未执行的请求。主请求被确定后该请求就会被加入到捎带队列中,同时改楼层按钮被按下;代码实例如下
if(this.station[1]==2){ //表示电梯之前是向下运行的,所以找出楼层最低的作为主请求
for(num=1;num<=10;) {
if(this.on_way[num][0][0]==0)
num++;
else
break;
} //判断捎带队列中的哪一个作为下一个主请求
if(num==11) { //num==11.表示捎带队列已经空了,所以将请求队列中的下一个作为主请求,并加入到捎带队列中
while(this.request_n[i][4]!=0)
i++; //表示在请求队列中将已经执行的队列过滤掉
this.on_way[0][0] = this.request_n[i];
this.pr_instr = this.request_n[i];
this.on_way[(int) this.request_n[i][0]][0] = this.request_n[i];//表示主请求的哪一个楼层的请求
this.request_n[i][4] = i+1;
}
else {
this.on_way[0][0] = this.on_way[num][0]; //第0个表示主请求
this.pr_instr = this.on_way[num][0];
}
}//如果是下行就找到最远的哪一个作为主请求,也就是楼层最小的那个作为朱请求
// ②
else if(this.station[1]==1){ //表示电梯之前是向上运行的
for(num=10;num>=1;) {
if(this.on_way[num][0][0]==0)
num--;
else
break;
} //判断捎带队列中的哪一个作为下一个主请求
if(num==0) { //num==0.表示捎带队列已经空了,所以将请求队列中下一个作为主请求,并加入到捎带队列中
while(this.request_n[i][4]!=0)
i++; //表示在请求队列中将已经执行的队列过滤掉
this.on_way[0][0] = this.request_n[i];
this.pr_instr = this.request_n[i];
this.on_way[(int) this.request_n[i][0]][0] = this.request_n[i];//表示主请求的哪一个楼层的请求
this.request_n[i][4] = i+1;
}
else {
this.on_way[0][0] = this.on_way[num][0]; //表示上行的时候将楼层最大的作为主请求
this.pr_instr= this.on_way[num][0];
}
}
// ③,表示之前电梯是静止的
else {
while(this.request_n[i][4]!=0)
i++; //表示在请求队列中将已经执行的队列过滤掉
this.on_way[0][0] = this.request_n[i];
this.pr_instr = this.request_n[i];
this.on_way[(int) this.request_n[i][0]][0] = this.request_n[i];//表示主请求的哪一个楼层的请求
this.request_n[i][4] = i+1;
}
if(this.pr_instr[3]==1) { //表示上行的
this.occupy[(int) this.pr_instr[0]][2]=1;
}
else if(this.pr_instr[3]==2) { //表示下行
this.occupy[(int) this.pr_instr[0]][3]=1;
}
else if(this.pr_instr[2]==2) { //表示在电梯内
this.occupy[(int) this.pr_instr[0]][1]=1;
}
}
判断同质请求会很麻烦
同质请求的判断稍不留神就出错了,小编也是打了好几次补丁才最终完成同质请求的判断,因为同质请求牵涉到的属性有点多;楼层,时间,请求队列,捎带队列,这些都会影响到同质请求的判断,那么到底什么样的请求才算是同质请求呢?以及判断为什么会如此的麻烦呢?
- 同质请求意味着在请求到达目标楼层之前,对于目标楼层发出了多个相同请求,小编在室友的指导下,采用了他的方法,楼层的按钮。同一楼层只有三种按钮,当该楼层的按钮被按下以后,在该楼层的请求完成之前,按钮都处于按下的状态,也就意味着继续按同一按钮是没有意义的,所以这样来判断同质
- 关于同质牵涉到的时间问题是,电梯在运动过程中,途中电梯有捎带请求执行的时候,他需要执行捎带请求,所以时间会向后延长1s,所以在计算同质请求的时候,时间要计算到判断同质请求的那一楼层
见代码:
for(floor=this.station[0];floor < this.request_n[k][0];floor++) {
if(this.on_way[floor][0][0]!=0) {
arrive++;
}
arrive+=0.5;
}
//计算出从当前楼层到队列中一层的时间
if(this.occupy[(int) this.request_n[k][0]][1]!=0 && (arrive+this.stop_time)>this.request_n[k][1]) //表示该层楼电梯内的按钮已经被占用
{
this.request_n[k][4] = 1;
System.out.printf("#SAME");
output2(this.request_n[k]); //在时间允许内,按钮已经被按下了,所以判断为同质请求
}
如何解决请求时间与执行时间
关于请求时间与执行时间的计算,有几点感觉需要注意,重要的是,时间与请求有关,还与主请求有关。对于当前时间,如果主请求时间晚于当前时间,那么电梯就要停止运行,等待主请求的到来,因为没有主请求,电梯是不会运行的。而电梯运行的途中,对于请求时间,需要计算从此次运行的开始时刻到达该楼层的时间。用来判断是否需要等待,如果需要等待,还需要在计算一次运行的时间,代码如下
if(state_in[0]==move[0]) {
if(this.in_way[this.state_in[0]][0][1]>this.st_time) {
this.st_time = this.in_way[this.state_in[0]][0][1];
}
this.st_time = this.st_time + this.mo_time;
}
else {
this.st_time = this.st_time + this.mo_time;
if(this.in_way[this.state_in[0]][0][1]>this.st_time) {
this.st_time = this.in_way[this.state_in[0]][0][1];
this.st_time = this.st_time + this.mo_time;
}
}
this.mo_time = 0; //判断是否是主指令
总结
对于本题我的类图如下:
代码耦合度:
小小收获
对于本课程收获,前三周虽然过的十分十分的辛苦,但是收获还是有的。感觉从来没有de过这么久的bug,没有写过这么久的bug,没有从头速成写代码。这些都是第一次。还有就是在计组课程之上对于设计理解了更多。关于设计思路,方法等,所以一般会在写代码之前写readme。对于整体的设计还是要更加的注重细节的处理,多在细节上花功夫,会使debug的过程简单很多。另一方面,互测虽然不是很“友好”,但是在看别人的代码中,收获了很多,代码的风格,处理问题的方式,等等。