问题描述

创建学生信息管理系统,具体要求如下:

学生信息包括:学号 姓名 数学成绩 英语成绩 计算机成绩

  • 功能1:添加学生信息 执行1时,输入学号,姓名,三门科目成绩;如果添加学生成功则输出“Add success”,如果学生已存在则输出“Students already exist”
  • 功能2:删除学生信息 执行2时,输入学号信息;如果学生不存在,输出“Students do not exist”,如果存在,则输出“Delete success”
  • 功能3:更改学生成绩信息 执行3时,输入学号信息;如果学生不存在,输出“Students do not exist”,如果存在,输出“Update success”
  • 功能4:显示学生平均分成绩 执行4时,输入学号信息;如果学生不存在,输出“Students do not exist”,如果存在,则输出学生信息,如下格式:其中平均分为三门科目相加除以3,保留一位小数,每行之间换行。
    • Student ID:2019989890
    • Name:Jerry
    • Average Score:89.3

问题求解:

  • 构建一个学生信息管理系统,实现上述功能。
  • 输入数据:第一行为一个整数n(0<n<130),后边共n行,每一行表示执行一种功能。其中1,2,3,4分别对应执行上面4种功能,具体格式见输入样例。 测试用例保证:学号和名字均为长度不超过10的字符串,各门课成绩为0到100之间的整数。

抽象模型

通常,我们遇到的现实世界中的问题都是采用自然语言描述的。当问题比较复杂时,其语义就会比较模糊。因此,我们需要采用数学语言来为之建立抽象模型,明确问题的确切含义。

典型的问题求解方式:

  • 问题 \(\Rightarrow\) 抽象模型 \(\Rightarrow\) 模型中求解 \(\Rightarrow\) 对解的解释(问题的解)
    • 这是一个典型的信息系统或信息管理系统。通过针对系统的一系列操作实现信息的存储、检索、处理。典型的操作一般包括:增加、删除、修改、查询。操作时通常还会附带一定的附加条件。

如何设计信息系统?

  • 信息包括哪些量?
    • 学生信息包括:学号 姓名 数学成绩 英语成绩 计算机成绩
    • 学号本质上是数值,但是由于长度可能到10位,无法用int表示,所以采用字符串来描述。
typedef struct Student
{
    char    ID[11];
    char    name[11];
    int     math;
    int     eng;
    int     cs;
} STUDENT;
  • 系统最多能存储多少学生信息?
    • 第一行为一个整数n(0<n<130),后边共n行,每一行表示执行一种功能。从这里看出,最多129次操作,如果都是增加,则系统需要存储129位学生的信息。
    • 一种典型的方法是采用数组来存储多个学生的信息,这里就是结构体数组。如果要考虑可扩展性,我们可以设置一个SIZE = 129,或者更大的数值。

STUDENT stu[SIZE];

  • 系统如何设计,影响到具体操作如何实现!

进一步思考

  • 数组本质上是一个有前后顺序的线性表。要不要排序?
    • 排序的话,查询会很快。插入(增加)时需要腾位子,删除时也需要压缩位子。
    • 不排序的话,查询只能线性查,插入可以在尾部,删除依然需要压缩位子。
  • 题目没有要求,我们可以选择不排序。
    • 注意:不同的选择,会影响到后续功能的实现方式!

模型中求解

信息管理系统,后续操作之间没有必然关联。因此,可以独立处理每一个操作。

  • 在信息系统中进行操作,本质上就是在实现数组中查找、添加、删除、更新、输出操作。

但是,注意到每个操作都需要查看一下这个记录是否已经存在。所以,可以把这个共同的东西抽取出来,用一个函数来实现。这就是一种复用。

int locate(STUDENT stu[SIZE], char sid[LEN], int count);

为了在系统中查找记录,我们需要知道表中到底有多少学生记录。也就是数组中有多少元素是学生记录,有多少元素是空着的。所以,引入一个变量来存放:

int count;

每次增加或删除操作后,需要对这个变量进行调整。

  • 注意:每个操作都需要判断记录是否存在,并据此进行相应的动作。特别是,当不需要进一步操作时,如果还有输入数据未被读取,需要读取后丢弃!

编码

基于上述分析,可以着手编码工作。

  •  代码级的设计包括:函数原型设计、变量名命名、变量类型设计、状态变量设计
    • 函数:不同功能打包成不同的函数,共同的功能设计为一个函数供上层函数调用。
      • addRecord
      • deleteRocrd
      • updateRecord
      • printRecord
      • locate
    • 结构体类型:STUDENT
    • 宏定义常量:SIZE,LEN
    • 变量:系统中记录数count,操作符op,学号sid
    • addRecoeddeleteRecord的第三个参数是int *count,因为在函数中需要对count作调整。
  • 输入数据处理:
    • scanf应该可以处理一切。
      • 是否能用"%s"读取姓名?
      • 是否需要用getchar来处理whitespace
  • 完整的C语言代码
#include <stdio.h>
#include <string.h>
#define SIZE 129
#define LEN 11

// student record structure
typedef struct Student
{
    char    ID[LEN];
    char    name[LEN];
    int     math;
    int     eng;
    int     cs;
} STUDENT;

// check if the record is in the system
// nedded by other 4 functions
int locate(STUDENT stu[SIZE], char sid[LEN], int count);

// Followings are 4 functionalities
void addRecord(STUDENT stu[SIZE], char sid[LEN], int *count);
void deleteRecord(STUDENT stu[SIZE], char sid[LEN], int *count);
void updateRecord(STUDENT stu[SIZE], char sid[LEN], int count);
void printRecord(STUDENT stu[SIZE], char sid[LEN], int count);

int main()
{
    STUDENT stu[SIZE];
    int op;
    char sid[LEN];
    int i, n;
    int count = 0;      // Total number of records in the system

    scanf("%d", &n);    // how many actions to do

    for (i = 0; i < n; i++)
    {
        // read in ops and studentID in order to determine what to do
        scanf("%d%s", &op, sid);
        if (op == 1)
        {
            addRecord(stu, sid, &count);
        }
        else if (op == 2)
        {
            deleteRecord(stu, sid, &count);
        }
        else if (op == 3)
        {
            updateRecord(stu, sid, count);
        }
        else    // op == 4
        {
            printRecord(stu, sid, count);
        }
    }

    return 0;
}

int locate(STUDENT stu[SIZE], char sid[LEN], int count)
{
    int loc = 0;
    while (loc < count)
    {
        if (strcmp(stu[loc].ID, sid) == 0)
            return loc;
        loc++;
    }
    return -1;      // means no record
}

void addRecord(STUDENT stu[SIZE], char sid[LEN], int *count)
{
    int loc;
    loc = locate(stu, sid, *count);
    // read and put the record at the end of the array temporarily
    // NECESSARY!!! even if the record already exists
    strcpy(stu[*count].ID, sid);
    scanf("%s", stu[*count].name);
    scanf("%d%d%d", &stu[*count].math, &stu[*count].eng, &stu[*count].cs);
    if (loc == -1)
    {
        // if not already exist, increment count to make the record valid
        *count += 1;
        printf("Add success\n");
    }
    else
    {
        printf("Students already exist\n");
    }
}
void deleteRecord(STUDENT stu[SIZE], char sid[LEN], int *count)
{
    int i, loc;
    loc = locate(stu, sid, *count);
    if (loc != -1)
    {
        // if the record exists, delete it
        // by shift the following records one index up,
        // and then decrement count by 1
        for (i = loc; i < *count - 1; i++)
        {
            stu[i] = stu[i + 1];
        }
        *count -= 1;
        printf("Delete success\n");
    }
    else
    {
        printf("Students do not exist\n");
    }
}
void updateRecord(STUDENT stu[SIZE], char sid[LEN], int count)
{
    int loc;
    loc = locate(stu, sid, count);
    if (loc != -1)
    {
        // if the record exists, update by reading new values
        scanf("%d%d%d", &stu[loc].math, &stu[loc].eng, &stu[loc].cs);
        printf("Update success\n");
    }
    else
    {
        // abandon input value by putting them after the end of list
        // NECESSARY!!!
        scanf("%d%d%d", &stu[count].math, &stu[count].eng, &stu[count].cs);
        printf("Students do not exist\n");
    }
}
void printRecord(STUDENT stu[SIZE], char sid[LEN], int count)
{
    int loc;
    loc = locate(stu, sid, count);
    if (loc != -1)
    {
        // if record exists, print out student info
        printf("Student ID:%s\nName:%s\n", stu[loc].ID, stu[loc].name);
        printf("Average Score:%.1f\n", (stu[loc].math + stu[loc].eng + stu[loc].cs) / 3.0);
    }
    else
    {
        printf("Students do not exist\n");
    }
}

另一个版本:

https://onlinegdb.com/DZlIObbin

各位可以看看,你认为哪个版本更好?为什么?

另外,是否还可以做一些小的改善呢?

测试用例

  • 边界测试
  • 增加新记录
  • 试图增加已存在的记录
  • 删除存在的记录
  • 试图删除不存在的记录
  • 更新存在的记录
  • 试图更新不存在的记录
  • 打印存在的记录
  • 试图打印不存在的记录
  • 先增加后删除,再。。。

总结

  • 这是一个典型的信息管理系统,主要功能就是增、删、改、查。

进一步讨论

 

You have no rights to post comments