第2章 程序员下午试题分析与解答 试题一 阅读以下说明和算法,完善算法并回答问题,将解答写在答题纸的对应栏内。 【说明】 假设以二维数组G[1..m,1..n)表示一幅图像各像素的颜色,则G[i,j]表示区域中点(i,j)处的颜色,颜色值为0~k的整数。 下面的算法将指定点(i0,j0)所在的同色邻接区域的颜色置换为给定的颜色值。约定所有与点(i0,j0)同色的上、下、左、右可连通的点组成同色邻接区域。 例如,一幅8×9像素的图像如图2-1所示。设用户指定点(3,5),其颜色值为0,此时其上方(2,5)、下方(4,5)、右方(3,6)邻接点的颜色值都为0,因此这些点属于点(3,5)所在的同色邻接区域,再从上、下、左、右四个方向进行扩展,可得出该同色邻接区域的其他点(见图2-1中的阴影部分)。将上述同色区域的颜色替换为颜色值7所得的新图像如图2-2所示。 【算法】 输入:矩阵G,点的坐标(i0,j0),新颜色值newcolor。 输出:点(i0,j0)所在同色邻接区域的颜色置换为newcolor之后的矩阵G。 算法步骤(为规范算法,规定该算法只在第七步后结束)如下。 第一步:若点(i0,j0)的颜色值与新颜色值newcolor相同,则 (1) ; 第二步:点(i0,j0)的颜色值→oldcolon创建栈S,并将点坐标(i0,j0)入栈; 第三步;若 (2) ,则转第七步; 第四步;栈顶元素出栈→(x,y),并 (3) ; 第五步;1)若点(x,y-1)在图像中且G[x,y-1]等于oldcolor,则(x,y-1)入栈S; 2)若点(x,y+1)在图像中且GIx,y+1]等于oldeolor,则(x,y+1)入栈S; 3)若点(x-1,y)在图像中且G[x-1,y)等于oldcolor,则(x-1,y)入栈S; 4)若点(x+1,y)在图像中且G[x+1,y)等于oldcolor,则(x+1,y)入栈S; 第六步:转 (4) ; 第七步:算法结束。 【问题】 是否可以将算法中的栈换成队列?回答; (5) 。 试题一分析 本题考查栈结构在算法中的应用。 栈或(和)队列常在某些应用中用来临时存储需要处理的元素,因此,其基本应用方式为:首先令一个(或多个)元素入栈(队列),然后在栈(队列)非空的情况下,栈顶(队头)元素出栈(队列)并进行处理,然后令与该栈顶(队头)元素相关的其他元素入栈(队列),再从判栈(队列)空开始重复以上过程。 根据题目说明部分的描述,所有与点(i0,j0)同色的上、下、左、右可连通的点组成同色邻接区域。要置换一个同色邻接区域中所有点的颜色,可先将所有需要改变颜色的点的坐标记录下来,然后逐个地改变其颜色值;也可采取找出一个点、处理一个点的方式进行颜色置换。题中给出的算法属于后一种情况。 显然,算法中需要一个存储空间,用于临时存储需要置换颜色的点的坐标,使每个需要处理的元素都进、出该存储区域一次,算法中的栈起的就是这个作用。实际上,对区域中各点的颜色置换的顺序是无关紧要的,因此,将算法中的栈换成队列不会影响算法的输出。 在本题中,若新的颜色值与同色区域中的原颜色相同,则无需置换。因此空 (1)处应填入“转第七步”。算法第二步先记下点(i0,j0)所在区域的原颜色,并令点(i0,j0)入栈,之后就是基于栈非空的操作了,因此空(2)处应填入“栈S为空”。第三步令栈顶元素出栈并修改对应点的颜色值,空(3)处应填入“修改(x,y)处的颜色值为newcolor"。算法中必然有一步能使算法步骤循环处理,因此第六步中的空(4)处应填入“第三步”。 参考答案 (1)转第七步 (2)栈S空,或等价的文字描述 (3)G[x,y]←newcolor,或G[x,y]=newcolor,或等价的文字描述 (4)第三步 (5)可以 试题二 阅读以下说明和C函数,将应填入 (n) 处的字句写在答题纸的对应栏内。 【说明】 某单位准备进行一次选举,对指定的n名(n<80)候选人进行投票。为了简化选举工作,事先印制了写有这n名候选人姓名的选票,投票者只需将选中者名下的圆圈涂黑即可。规定每张选票上被涂黑的圆圈数不得超过3个,也不得少于1个,否则视为无效选票。投票结束后,所有选票经计算机扫描处理,形成了描述选票状态的文本文件。例如,n=8时所形成的文件如下: 01011000 10010100 10011000 11111111 00000000 00111000 其中,每行表示一张选票的数据,每列代表一位候选者的得票情况。第i行第j列为1,表示第i张选票上投了第j名候选人1票。 函数statisdc()的功能是读入选票数据,并统计每位候选者的得票数,函数返回有效选票数。 【C函数】 int statistic(FILE*fp,int candidatet[],int n) { /*从文件中读入选票数据,n为候选人数量(n<80),从有效选票中统计每位候选者的得 票数并存入candidate[],函数返回有效选票数*/ char str[80]; /*str保存读入的一张选票数据*/ int i,tag=0; /* tag用于判断每张选票是否有效*/ int q=0; /*q用于计算有效选票数*/ for(i=0;i<n; i++) candidate[i]=0; while( (1) ){ fgets(str,80,fP); /*读入一张选票数据*/ for(tag=0,i=0; (2) ;i++) if (str[i]=='1') tag++; if( (3) ){/*若是有效选票,则进行统计*/ (4) ; for(i=0; i<n; i++) if.(str[i]=='1') (5) ; } } return q; }/*statistic*/ 试题二分析 本题考查C语言中的文件操作和简单数据处理。 C语言提供了大量的库函数,用于创建和使用数据文件。 在C语言中,必须在处理数据文件(甚至创建数据文件)之前将其打开。打开一个文件的库函数为 fopen(文件名,打开方式); 其中,文件名包含路径,以字符串形式给出,函数返回一个指针。打开方式如下表所示:
关闭文件的函数为fclose(文件指针)。 本题目中将文件指针作为参数传递给函数statistic(),表明文件已经打开,无须在该函数中打开和关闭文件。 根据文件打开之后的用途,可将进行读/写操作的数据文件分成两类:文本文件和二进制文件。 文本文件有一个结束标志(最后一个字符是码值为26的ASCII字符),读取函数在文件中发现该字符后向调用程序返回一个EOF。 文件指针为fp,feof(fp)用来测试fp所指向的文件当前状态是否“文件结束”,若是,则函数返回值为1,否则返回值为0。如果/顷序读入文本文件fp中的数据,每次读取一个字符,则代码如下: while(!feof(fp)) { ch=fgetc(fp); . . . } 因此,函数中空(1)处应填入“!feof(fp)”。 fgets()的作用是从指定文件中读入一个字符串,函数调用方式为“fgets(str,n,fp)”,从fp指向的文件输入n-1个字符,并把它们放到字符数组str中,如果在读入n-1个字符结束之前遇到换行符或EOF,则结束读入。 根据数据文件的内容安排,函数中通过语句“fgets(str,80,fp);”每次读入一行数据(一张选票的信息),str[i]为第i个候选人在本张选票上的得票情况。 题目中规定每张选票上被涂黑的圆圈数不得超过3个,也不得少于1个,否则视为无效选票。因此,对于读到的选票数据,首先应判断其是否有效。函数statistic()中的以下代码用于计算一张选票中“1”的个数,从而判断其有效性。 for(tag=0,i=0; (2) ;i++)/* tag用于判断每张选票是否有效*/ if (str[i]=='1') tag++ 当tag>3或tag<1时,选票无效。 题中已声明候选人数小于80并且通过参数传入了候选人的个数n,因此i的值应小于n,结合串结束标志“\0”将对数组str的元素的访问控制在有效下标范围内,因此空 (2)处填入“i<n&& str[i]!='\0',空(3)处应填入“tag>=1 && tag<=3”。 根据要求,还应计算有效选票的数量,因此空(4)处应填入“q++”。 若str[i]等于1,说明候选人i在本张选票上得了一票,所以空(5)处应填入“candidate[i]++'’(在计算票数之前,函数中应将每个候选人的初始得票数设置为0。) 参考答案 (1)!feof(fp) (2)i<n && str[i]!='\0',或其等价形式 (3)tag>=1 && tag<=3,或其等价形式 (4)q++,或其等价形式 (5)candidate[i]++,或其等价形式 试题三 阅读以下说明和C函数,将应填入 (n) 处的字句写在答题纸的对应栏内。 【说明】 若一个矩阵中的非零元素数目很少且分布没有规律,则称之为稀疏矩阵。对于m行 n列的稀疏矩阵M,进行转置运算后得到n行m列的矩阵MT,如图2-3所示。 为了压缩稀疏矩阵的存储空间,用三元组(即元素所在的行号、列号和元素值)表示稀疏矩阵中的一个非零元素,再用一维数组逐行存储稀疏矩阵中的所有非零元素(也 称为三元组顺序表)。例如,图2-3所示的矩阵M相应的三元组顺序表如表2-1所示,其转置矩阵MT的三元组顺序表如表2-2所示。 表2-1
表2-2
函数TransposeMatrix(Matrix M)的功能是对用三元组顺序表表示的稀疏矩阵M进行转置运算。 对M实施转置运算时,为了将M中的每个非零元素直接存入其转置矩阵MT三元组顺序表的相应位置,需先计算M中每一列非零元素的数目(即MT中每一行非零元素的数目),并记录在向量num中;然后根据以卞关系,计算出矩阵M中每列的第一个非零元素在转置矩阵MT三元组顺序表中的位置。 cpot[0]=0 cpot[j]=cpot[j-1)+num[j-1]/*j为列号*/ 类型ElemType、Triple和Matnx定义如下: typedef int ElemType; typedef struct{ /*三元组类型*/ int r,c; /*矩阵元素的行号、列号*/ ElemType e; /*矩阵元素的值*/ }Triple; typedef struct{ /*矩阵的三元组顺序表存储结构*/ int rows,cols,elements;/*矩阵的行数、列数和非零元素数目*/ Triple data [MAXSIZE]; }Matrix; 【C函数】 int TransposeMatrix (Matrix M) { int j,q,t; int *num, *spot; Matrix MT; /* MT是M的转置矩阵*/ num= (int *)malloc(M.cols*sizeof(int)); cpot= (int *)malloc(M.cols*sizeof(int)); if (!num || !cpot) return ERROR; MT.rows = (1) ; /* 设置转置矩阵MT行数、列数和非零元数目*/ MT.cols = (2) ; MT. elements = M. elements; if (M.elements > 0) { for(q = 0; q < M.cols; q++) num[q] = 0; for(t = 0; t < M.elements; ++t) /*计算机阵M中每一列非零元素数目 */ num.[M.data [t] . c] ++; /*计算矩阵M中第列第一个非零元素在其转置矩阵三元组顺序表中的位置*/ (3) ; for(j = 1;j < M.cols; j++) spot [j] = (4) ; for(t = 0; t < M.elements;t++){ j = (5) ; /* 取矩阵M的一个非零元素的列号存入j*/ q = cpot[j]; MT.data[q].r = M.data[t].c; MT.data[q].c = M.data[t].r; MT.data[q].e = M.data[t] .e; ++cpot[j]; /* 计算M中第j列的下一个非零元素的目的位置*/ }/* for */ }/* if */ free(num);free(cpot); /*此处输出矩阵元素,代码省略*/ return OK; )/*TransposeMatrix*/ 试题三分析 本题考查稀疏矩阵的压缩存储及转置运算在压缩存储结构上的实现。虽然从题干部分的描述来看,有一定的难度,但代码中要求完善的部分却很简单,因此,题目要求的是以对应于算法描述的方式机械地实现程序。 转置矩阵的行数、列数分别等于原矩阵的列数、行数,转置矩阵第i行第j列的元素就是原矩阵第j行第i列的元素。 若采用二维数组存储矩阵元素,则矩阵的转置运算是及其简单的。例如,已知有m行n列的矩阵M矩阵,求其转置矩阵MT,则常见的代码形式如下: for(i=0; i<m; i++ ) for(j=0;j<n;j++) MT[j,i) ;M[i,j]; 当采用三元组表顺序存储稀疏矩阵中的非零元素时,简单交换每个元素的行号和列号即可实现转置运算,但是转置后元素的存储顺序发生了变化,原矩阵的非零元素按行存储,转置以后则按列存储,如下表所示。
因此,还需要对转置矩阵MT的三元组表按行号进行排序。 本题目中给出的方法是:在进行转置运算时就安排好每个元素在其转置矩阵中按行存储时的位置,这样就需要一些辅助运算,即先计算M中每一列非零元素的数目(即 MT中每一行非零元素的数目),并记录在向量num中,然后计算元素在转置矩阵中三元组表的位置。 在程序中以下代码计算矩阵M每列的元素数目: for(q=0; q<M.cols;q++) /*初始化向量num*/ num[q]=0; for(t=0;t<N.elements;++t)/*计算矩阵M中每一列非零元素数目*/ num[M.data[t].c]++; /*M.data[t).c为M中第t个非零元素的列号*/ 以题中所给的矩阵为例,经计算后,向量num[]={1,2,0,1,2),即第0列有一个非零元素,第1列有两个非零元素,第2列没有非零元素,依此类推。 计算出向量num[]后,根据以下关系,可计算出矩阵M中每列的第一个非零元素在转置矩阵MT三元组顺序表中的位置。 cpot[0]=o /*设置M的第0列的第一个非零元素在MT三元组表中的位置*/ cpot[j]=cpot[j-1]+ num[j-1l /*j为列号*/ 第j-1列第一个非零元素的位置加上本列非零元素的个数即是第j列的第一个非零元素在MT三元组表中的位置。 参考答案 (1)M.cols (2)M.rows (3)cpot[0]=0,或其等价形式 (4)cpot[j-1]+num[j-1],或其等价形式 (5)M.data[t].c,或其等价形式 试题四 阅读以下应用说明以及用Visual Basic语言编写的程序代码,将应填入 (n) 处的字句写在答题纸的对应栏内。 【应用4.1】 设应用程序的运行窗口内有一个文字标签(Label)以及一个框架,其中有三个复选框(chkl,chk2,chk3),各个复选框单击事件过程的程序代码如下: Private Sub chkl_Click() Label.fontBold=chkl.Value End Sub Private Sub chk2_Click() Label.fontltalic=chk2.Value End Sub Private Sub chk3_Click() Label.fontUnderLine=chk3.Value End Sub 三个复选框chkl、chk2、chk3的功能分别是: (1) 。 【应用4.2】 设应用程序的运行窗口内有两个文本框Txt1和Txt2,其初始内容为空。在Txt1文本框中输入一个数值,当光标离开此文本框(例如进入文本框Txt2)时,执行的程序代码如下: Private Sub Txt1_LostFocus() dim x aS double x=Val(Txtl.Text) 工f x<0 Or x>100 Then Txtl.Text ="" MsgBox$("请重新输入!") Txtl.SetFocus Else Txt2.Text=Txt1.Text End If End Sub 该程序代码的功能是:若在文本框Txtl中输入的数值小于0或大于100,当光标离开此文本框时, (2) ;否则,将其值复制到文本框Txt2中。 【应用4.3】 在下面的应用中,当窗口内发生C1ick事件时,窗口内将显示如图2-4所示的杨辉三角(每一行都是二项式展开的系数)。请完善程序代码。 Private Sub Form Click() Dim i,j,c As Integer,StrTemp AS String Dim a(9)As Integer a(0)=0=a(1)=l:StrTemp=Str(a(1))+Space(3) CurrentX=(ScaleWidth-TextWidth(StrTemp))/2 Print StrTemp For j=2 TO 9 a(j)=1 For C=j-1 TO 2 Step-1 a(c) = (3) Next (4) ="" For c = 1 To j StrTemp = StrTemp & Str( (5) ) & Space(5 - Len(Str(a(c)))) Next CurrentX = (ScaleWidth - TextWidth(StrTemp)) / 2 Print StrTemp Next End Sub 试题四分析 本题考查Visual Basic中常用控件的属性设置及事件处理。 对于同一框架中的一组单选按钮,只能有一个被选中,用户选中一个时,另一个就自动呈现取消选中状态。这种效果是系统自动赋予的,无须编程。对于复选框,则可以同时选中多个,每个复选框是独立选择的,单击复选框就是改变该复选框的选中状态。因此,复选框的Click事件的程序代码中,还需要判断是选中还是取消选中。 单选钮的属性为Value,其值为True表示选中,为False表示未选中(默认)。 复选框的属性为Value,其值为0表示未选中(默认),1表示选中,2表示禁用。 fontBold、fontltalic和fontUnderLine分别表示文字的粗体属性、斜体属性和下划线属性,因此应用4.1中,复选框chk1、chk2、chk3分别控制文字标签中文字的粗体、斜体和下划线设置。 应用4.2程序代码的功能是:若在文本框Txt1中输入的数值小于0或大于100,当光标离开此文本框时,将该文本框清空,弹出对话框并提示重新输入,光标定位于该文本框中;否则,将其值复制到文本框Txt2中。 应用4.3在窗体中显示杨辉三角(每一行都是二项式展开的系数),观察元素的值可知,值不等于1的元素由其左上角和右上角的元素相加得到。例如,若已经计算出第四行的所有元素,如下所示,然后需求出第五行的所有元素,每行的元素存储在一维数组a中。 a(1) a(2) a(3) a(4) 第四行:1 3 3 1 第五行上的元素自左至右表示为a(1)、a(2)、a(3)、a(4)、a(5),如下所示。 a(1) a(2) a(3) a(4) 第四行: 1 3 3 1 第五行:1 4 6 4 1 a(1) a(2) a(3) a(4) a(5) 先设置最后一个元素a(5)等于1,再根据第四行上的a(4)和a(3)求出第五行上的a(4), 根据第四行的a(3)和a(2)求出第五行上的a(3),依此类推,可求出第五行的每个元素(每行的第一个元素等于1,无需另外计算)。 推广至其他行,可计算出杨辉三角中的每个数并以串的方式输出,对应的程序代码如下: For j=2 To 9 '计算出第2行到第9行的每个数并输出 a(j)=1 '每行的最后一个数为1 For c=j-1 To 2 Step-1 a(c)=a(c)+a(c-1) '计算出值不等于1的元素 Next StrTemp="" '字符串初始化,每行所有数串接成一个字符串输出 For C=1 To j StrTemp=StrTemp & Str(a(c)) & Space(5-Len(Str(a(C)))) Next CurrentX=(ScaleWidth-TextWidth(StrTemp))/2 Print StrTemp Next 参考答案 (1)对文字标签中的文字,分别控制其粗体、斜体和下划线设置;或含义相同的文字描述 (2)将该文本框清空:弹出对话框,提示重新输入:光标定位于该文本框中;或含义相同的文字描述 (3)a(c)+a(c-1) (4)StrTemp (5) a(c) 试题五 阅读以下说明和C函数,将应填入 (n) 处的字句写在答题纸的对应栏内。 【说明】 某班级有N名学生,他们可根据自己的情况选修名称和数量不尽相同的课程。设N等于6,学生信息、所选课程及成绩用链表结构存储,如图2-5所示。 程序中相应的类型定义如下: #define N 6 struct node{ char cname[5]; /*课程名*/ int grade; /*成绩*/ struct node *next; /*指针,指示某学生选修的下一门课程及成绩*/ }; struct student{ char xh[5]; /*学号*/ char name[20]; /*姓名*/ struct node*link; /*指针,指示出选修的课程及成绩链表*/ }stud_info[N]; smd_mb[]为一个全局数组。 函数func(char kc[],int*num)的功能是统计选修了课程名为kc的学生的人数,并返回该课程的平均成绩(若无人选修该课程,则平均成绩为0),参数num带回选修课程比的学生人数。 【C函数】 double func(char kc[],int*num) { int i,count=0,sum=0; /*count用于记录选修课程名为kc的学生的人数*/ double avg=0.0; struct node *p; for(i=0;i<N;i++){ p= (1) ; /*取第i个学生所修课程链表的头指针*/ while (p) { if( (2) ){ sum= (3) ; count++; break;; }/*if*/ p=p->next; }/*while*/ } (4) ; if( (5) ) avg=(double)sum/count; /*计算平均成绩*/ return avg; }/*func*/ 试题五分析 本题考查链表的查找运算。 函数time(char kc[],int*num)的功能是统计选修了课程名为kc的学生的人数,并返回该课程的平均成绩(若无人选修该课程,则平均成绩为0),参数num返回选修课程 kc的学生人数。 函数中用count计算选修课程kc的人数、用sum记录学生该课程的成绩总和<除以学生人数即可得到平均成绩)。 stud_info[]为一个全局数组,stud_info[0]是第0个学生的信息,stud_info[1]是第1个学生的信息,依次类推。 对于每个学生,用一个单链表存储其选修的课程及成绩,链表的头指针为smd info[i] link,显然,应遍历为每个学生建立的单链表。空(1)处应填入“stud info[i].link”。 对于链表中的一个结点,应查看其课程名是否等于给定的课程名kc,若相等,则将其成绩累加至sum。因此空(2)处应填入“!strcmp(p->cname,kc)”,空(3)处应填入“sum+p->grade"。 由于需要参数*num返回选修课程比的人数,所以空(4)处应填入“*num=count”。 平均成绩等于成绩总和除以选课人数,因此,选课人数应不等于0,所以空(5)处填入“count>0”或“count!=0”。 参考答案 (1)stud_info[i].link,或其等价形式 (2)!strcmp(p->cname,kc),或其等价形式 (3)sum+p->grade,或其等价形式 (4)*num=count,或其等价形式 (5)count!=0,或count>0,或其等价形式 试题六 阅读以下说明和C++程序代码,将应填入 (n) 处的字句写在答题纸的对应栏内。 【说明】 在下面的C++代码中,类SalesTicket能够完成打印票据正文的功能,类HeadDec- orator与FootDecorator分别能够完成打印票据的台头和脚注的功能。 已知该程序运行后的输出结果如下所示,请填补该程序代码中的空缺。 这是票据的台头! 这是票据正文! 这是票据的脚注! ---------------------- 这是票据的台头! 这是票据的脚注! 【C++程序代码】 #include <iostream> using namespace std; class SalesTicket { public: (1) printTicket() { cout << " 是票据正文!" << endl;} class Decorator : public SalesTicket{ SalesTicket *ticket; public: Decorator(SalesTicket *t){ ticket = t; } void printTicket(){ if(ticket != NULL) ticket->printTicket(); } }; class HeadDecorator : public Decorator{ public: HeadDecorator(SalesTicket *t): (2) { } void printTicket() { sour << "这是票据的台头!" << endl; Decorator::printTicket(); } }; class FootDecorator :public Decorator{ public: FootDecorator(SalesTicket *t): (3) void printTicket() { Decorator::printTicket(); cout << "这是票据的脚注!" << endl; } }; void main(void) { SalesTicket t; FootDecorator f(&t); HeadDecorator h( (4) ); h.printTicket(); cout << "-------------------------"<< endl; FootDecorator a(NULL); HeadDecorator b( (5) ); b.printTicket(); } 试题六分析 本题实际上使用了设计模式中的Decorator模式。 从代码可以分析得出,类SalesTicket完成了票据正文的输出,FootDceorator和 HeadDecorator类都继承了Decorator,Decorator继承SalesTicket,FootDecorator和HeadDe- corator类在构造函数的时候都需要对其父类进行初始化,因此,空(2)和(3)应为 Decorator(t);这些类中都具有printTicket方法,根据SalesTicket类中方法的定义可以得出,此方法的返回值为void类型,分析主程序中h.printTicket()可以输出“这是票据的台头!这是票据正文!这是票据的脚注!”的结果,并且考虑到h.printTieket()的执行路径为先执行cout<<"这是票据的台头!"<<endl;这一语句,然后执行Decorator∷printTicket(),因此,Decorator∷printTicket()将完成“这是票据正文!这是票据的脚注!”内容的输出,由于Decorator类的实例仅可以输出“这是票据正文!”,所以,当前运行到此处的实例是一个FootDecorator的对象,因此空(4)处应该为&f,同时要求调用Decorator类的 printTicket方法时能够执行其子类FootDecorator类的相应方法,因此,空(1)处应该为一个虚拟函数,所以空(1)的结果应为virtual void:同理,空(5)处应为&a; 参考答案 (1)virtual void (2)Decorator(t) (3)Decorator(t) (4)&f (5)&a 试题七 阅读以下应用说明以及用Visual Basic编写的程序代码,将应填入 (n) 处的字句写在答题纸的对应栏内。 【应用说明】 某应用程序用于监测某种设备的工作温度(20~200℃),其运行窗口中,包括一个温度计(矩形形状shpMeter)以及其中指示当前设备温度的水银柱(矩形形状shpT),文字标签标记了温度刻度;另有一个图片框picCurve,用于动态描述检测到的温度曲线 (用户见到的曲线与水银柱等高变化);命令按钮“开始检测”(cmdStart)用于启动温度检测,命令按钮“暂停检测”(emdStop)用于暂停检测。 矩形形状shpT(水银柱)属性visible初始设置为不可见,属性Filltype设置为solid (实心),FillColor设置为红色;图片框picCurve的属性AutoRedraw设置为Tree 再创建一个定时器TimT,属性Enabled初始设置为False(不起作用),属性Interval(定时间隔)设置为500ms。 为模拟设备温度的检测,程序中利用了(0,1)之间均匀分布的伪随机数获得[20,200]之间的随机温度T。为了便于在图片框picCurve中绘制曲线,程序中对该图片框建立了坐标系统,左上角为原点(0,0),水平向右方向为X轴,垂直向下方向为Y轴,右下角坐标为(50,180)。为了便于观察记录的温度值,图片框中从上到下创建了5条水平线Ls(i),i=0,1,…,4,并在程序中按等间隔排列进行位置设置。程序中每隔半秒算出曲线点(x,y),其中x=0,1,2,…,再用线段连接各相邻曲线点形成温度曲线,如下所示。 【 Visual Basic程序代码 】 Dim (1) As Integer /*声明全局变量*/ Private Sub CmdStart_Click( ) TimT.Enabled = True ShpT.Visible = True End Sub Private Sub CmdStop_Click( ) TimT.Enabled = False End Sub Private Sub Form Load( ) Dim i, H As Integer PicCurve.Scale(0,0)-(50,180) '设置图片框坐标系:左上角—右下角 H=30 'H等于图片框高度的六分之一 For i=0 To 4 '设置5条水平线Ls(i)的位置 Ls(i).X1=0 'Ls(i)起点横坐标 Ls(i).n=n* (2) 'Ls(±)起点纵坐标 Ls(i).x2=50 'Ls(i)终点横坐标 Ls(i).Y2=Ls(i).Y1 'Ls(i)终点纵坐标 Ls(i).BorderColor=&HCOCOC0 '设置水平线颜色 Next i x=0 '设置曲线坐标初值 End Sub Private Sub timT_Timer() Dim T,H As Integer 'T为即时温度,H为图片框中温度点显示高度 T=Int(Rnd*181)+20 '模拟随机产生设备温度(20~200℃) '按当前温度显示水银柱 H=ShpMeter.Height* (3) '算出水银柱的高度 ShpT.Top= (4) -H '设置水银柱顶部位置 ShpT.Height=H '设置水银柱的高度 '绘制温度曲线 y= (5) '算出曲线上当前点的纵坐标 If x=51 Then '当超出图片框时 PicCurve.Cis '清除图片框内以前画的曲线 x=0 '设置重画曲线的初值 Elself x>0 Then '除左边点外 PicCurve.Line (x-1,Lasty)-(x,y),vbRed ,由前1点到当前点画红色线段 End If x=x+1 '准备下一点坐标 Lasty=y '保存当前坐标供下次使用 End Sub 试题七分析 本题是从实际工作岗位应用项目中抽象出来并加以简化的例子。 用随机数模拟实时检测数据是常用的方法。Rnd是由计算机产生的均匀分布在(0,1)区间内的伪随机数。每调用一次就会产生一个新的数。随机取若干个数,观察其分布,就会发现其在(0,1)中比较均匀分布这种特性(不会过于集中在某个小段)。从而, Rnd*181均匀分布于(0,181),取整数部分后,Int(Pnd*181”均匀分布于0,1,…,180之间。T=Int(Rnd*181)+20均匀分布于20,21,…,200之间,因此,程序中可以用T值来模拟实时检测到的设备温度。 屏幕上的温度计形状控件shpMeter是固定的,其高度由其Height属性决定 (shpMeter.Height),其顶部离屏幕上边的距离由其top属性决定(shpMeter.Top)。由于温 度计形状的属性FillStyle默认为透明,因此,在其中创建水银柱时便可见。 水银柱的底部应固定在温度计的底部,水银柱的高度应随温度T的变化而改变。开发时要求:尚未开始检测温度时,水银柱应不可见;启动检测后,当温度T=20时,水银柱的高度为0;当温度T=200时,水银柱的高度应等于温度计的高度;对于其他温度,水银柱的高度应有线性变化。水银柱形状shpT在屏幕上的位置取决于以下三个属性: shpT.Top 顶部位置(随温度T的变化而变化) shpT.Height 高度H(随温度T的变化而变化) shp.Lefi 左边位置(固定不变) 水银柱的高度H应是温度T的线性函数,其端点值可以列表如下:
因此,按线性比例便有:(H-0)/(T-20)=(shpMeter.Height-0)/(200-20), 从而,H=ShpMeter.Height*(T-20)/180,所以(3)处应填入(T-20)/180。 水银柱的顶部离屏幕上边的距离应等于温度计离屏幕上边的距离加上水银柱顶部到温度计顶部的距离,而后者就是温度计的高度与水银柱的高度之差(如下图所示)。因此: shpT.top=shpMeter.Top+shpMeter.Height-H 所以,(4)处应填入ShpMeter.Top+ShpMeter.Height。 在该坐标系统内,图片框上边与下边的距离为180,中间用5条水平线将其等分,各线间的距离H=30(注意两个过程中用同样的局部变量H表示不同的含义)。 最上面的第0线Ls(0)的起点应为(0,H),终点应为(50,H)。 最下面的第4线Ls(4)的起点应为(0,5H),终点应为(50,5H)。 因此,一般的Ls(i)线的起点应为(0,(i+1)H),终点应为(50,(i+1)H),从而(2)处应填入(i+1)。 图片框中的曲线点离框下边的高度(在坐标系统内计算)应等于温度值T,但它不是图片框中的纵坐标值。温度T与相应的曲线纵坐标是线性关系,其两端值如下; 当T=20时,水银柱高度为0,相应的曲线点应位于图片框下边,其纵坐标应是180; 当T=200时,水银柱最高,相应的曲线点应位于图片框上边,其纵坐标应是0。 因此,温度为T时,相应曲线点的纵坐标值应是200-T。所以(5)处应填入200-T。 由于曲线点高度与水银柱高度两端点值相同,且都是线性变化,所以,曲线点与水银柱必然同时等高变化。 按定时器TimT设置的定时时间间隔500ms,每隔半秒钟就得到一个温度T的值。设图片框中以x=1,2,…表示逐点记录温度的横坐标,则每个曲线点的坐标就是(x, 200-T)。在这些曲线点中,凡是与x相邻的两点都用线段连接起来,就形成了温度曲线。 设当前的曲线点为(x,y),前一曲线点的坐标为(Lastx,Lasty),则Lastx=x-1。 当x=0时,计算出的只有一个曲线点(0,y),还不能连线,所以应将其值y保存在 Lasty中,以便在下个时刻计算时,作为前一点的坐标。 当1≤x≤50时,应该用线段连接(Lastx,LastY)与(x,y)两点。 当x=51(超出图片框边界)时,应清除图片框中已绘制的曲线,再以x=0开始处理。 程序中的第一行用于声明全局变量,变量x已在两个过程模块中使用,应作为全局变量。在定时器定时触发的过程中,Lasty需要保存起来供下次执行过程时使用。在Visual Basic中,这个变量应作为静态变量或全局变量。本应用程序中,没有设置静态变量,所以Lasty应作为全局变量。从而,(1)处应填入x,Lasty。 参考答案 (1)x,Lasty (2)(i+1),或其等价形式 (3)(T-20)/180,或其等价形式 (4)ShpMeter.Top+ShpMeter.Height (5)200-T 试题八 阅读以下说明和Java程序代码,将应填入 (n) 处的字句写在答题纸的对应栏内。 【说明】 在下面的Java程序代码中,类SalesTicket能够完成打印票据正文的功能,类 HeadDecorator与FootDecorator分别能够完成打印票据的台头和脚注的功能。 已知该程序运行后的输出结果如下所示,请填补该程序代码中的空缺。 这是票据的台头! 这是票据正文! ------------------- 这是票据的脚注! 这是票据的台头! 这是票据的脚注! 【tava程序代码】 public class SalesTicket { public void printTicket() { System. out. println ( "这是票据正文 ! "); } } public class Decorator extends SalesTicket{ SalesTicket ticket; public Decorator(SalesTicket t) { ticket = t; } public void printTicket(){ if(ticket != null) ticket.printTicket(); } public class HeadDecorator extends Decorator{ public HeadDecorator(SalesTicket t) { (1) } public void printTicket() { System. out.println("这是票据的台头! "); super.printTicket(); } } public class FootDecorator extends Decorator{ public FootDecorator(SalesTicket t) { (2) ; } public void printTicket() { super, printTicket (); System. out.println ("这是票据的脚注!"); } } public class public static void main(String[] args) { T = new HeadDecorator( (3) ); T. (4) ; System.out.println("------------------------------------"); T = new FootDecorator( (5) ); T.printTicket (); } } 试题八分析 本题中的类HeadDecorator和FootDecorator都继承了Decortator类,因此在构造函数中需要对Decorator类进行初始化,所以空(1)和(2)处应该为super(t)。类Main中空(3)和(4)处程序代码的输出为“这是票据的台头!这是票据正文!这是票据的脚注!”,由于T是一个headDecorator类的实例,因此空(4)处一定是printTicket方法的调用,类headDecorator将输出“这是票据的台头!”后调用其父类的printTicket方法,而仅仅只有FootDecorator类可以输出“这是票据的脚注!”,因此,当前父类的具体实例对象应该为FootDecorator的实例,所以空(3)处应该为new(FootDecorator(new SalesTicket))。空(5)同空(3)原理相同,但由于不需要输出票据的正文,因此在构造HeadDecorator对象的时候其参数为null即可。 参考答案 (1)super(t) (2)super(t) (3)new FootDecorator(new SalesTicket()) (4)printTicket() (5)new HeadDecorator(null) |
|