2024-2025-1 20241311 《计算机基础与程序设计》第八周学习总结
学期 2024-2025-1 学号 20241311 《计算机基础与程序设计》第八周学习总结
作业信息
这个作业属于哪个课程 | <班级的链接>(2024-2025-1-计算机基础与程序设计) |
---|---|
这个作业要求在哪里 | <作业要求的链接>(2024-2025-1计算机基础与程序设计第八周作业) |
这个作业的目标 | <写上具体方面> |
作业正文 | ... 本博客链接 |
教材学习内容总结
C语言程序设计第五版第七章学习总结
一、函数的概念与重要性
在C语言程序设计中,函数是实现模块化程序设计的重要工具。函数能够将复杂的程序分解成多个较小的、易于理解和管理的部分。每个函数负责完成一个特定的功能,这种结构化的设计方法使得代码更加清晰、精炼、简单和灵活。
二、函数的定义
函数定义包括以下几个部分:
-
函数返回类型:指定函数执行完毕后返回给调用者的数据类型。如果函数不返回任何值,则使用
void
类型。 -
函数名:用于标识函数,以便在需要时按名调用。
-
参数列表:包括参数的类型和名称,用于在函数调用时向函数内部传递数据。无参函数不需要参数列表,但括号不能省略。
-
函数体:包含完成特定功能的声明和语句。
例如,定义一个比较两个整数大小的函数:
int max(int x, int y) {
return x > y ? x : y;
}
三、函数的调用
调用函数之前,需要对其进行声明(对于用户自定义函数,且声明在调用之后的情况)。函数调用的形式主要有三种:
-
函数调用语句:函数调用单独作为一个语句,不要求函数带回值,只要求函数完成一定的操作。
-
函数表达式:函数调用出现在另一个表达式中,要求函数带回一个确定的值以参加表达式的运算。
-
函数参数:函数调用作为另一个函数调用时的实参。
四、实参和形参
- 实参:函数调用时传递给函数的实际参数,可以是常量、变量或表达式。
- 形参:函数定义中声明的参数,用于接收调用函数时传递的数据。在函数被调用时,形参才被临时分配内存单元。
实参向形参的数据传递是“值传递”,单向传递,只能由实参传给形参,而不能由形参传给实参。实参和形参在内存中占有不同的存储单元。
五、函数的嵌套调用与递归调用
- 嵌套调用:在调用一个函数的过程中,又调用另一个函数。
- 递归调用:在调用一个函数的过程中,直接或间接地调用该函数本身。递归调用必须有明确的终止条件,以防止无限递归导致程序崩溃。
六、数组与函数
数组元素可以用作函数实参,但不能用作形参。使用数组元素作为实参时,传递的是数组元素的值。使用数组名作为函数实参时,传递的是数组首元素的地址。
七、函数声明与函数原型
在调用用户自定义函数之前,如果函数定义在调用之后,则需要在调用点之前对函数进行声明。函数声明(也称为函数原型)的作用是告诉编译器函数的名称、参数类型和数量,以便编译器能够正确地进行类型检查和函数调用。
八、总结
第七章介绍了函数在C语言程序设计中的基本概念、定义、调用方式、参数传递机制、嵌套调用与递归调用、数组与函数的关系以及函数声明的重要性。通过掌握这些内容,可以更加有效地利用函数实现模块化程序设计,提高代码的可读性、可维护性和可重用性。
计算机科学概论第七版第七章学习总结
计算机科学概论第七版的第七章主要聚焦于“问题求解与算法设计”,这一章节深入探讨了计算机解决问题的过程、算法的设计与分析、以及几种重要的数据结构和排序算法。以下是对本章内容的详细总结:
一、问题求解过程
本章首先介绍了如何系统地解决问题,这一过程借鉴了George Polya在《How to Solve It: A New Aspect of Mathematical Method》中提出的方法,包括:
- 理解问题:明确问题的要求和限制条件。
- 设计方案:列出解决问题的主要步骤和所需资源。
- 执行方案:编写算法并运行以验证其正确性。
- 回顾与改进:对解决方案进行评估,必要时进行修改和优化。
二、算法设计基础
算法是解决问题的明确指令集合,本章强调了算法设计的重要性,并介绍了算法的一些基本特性,如有限性、确定性、输入、输出和可行性。
三、简单变量与复合数据结构
- 简单变量:不能被分解的单个值,如整数、浮点数等。
- 复合数据结构:包括数组、记录等,它们能够存储多个相关数据项。本章特别强调了数组作为有序数据集合的重要性,并介绍了顺序搜索和二分检索等搜索算法。
四、排序算法
排序是计算机科学中的基本问题之一,本章详细介绍了多种排序算法,包括:
- 选择排序:通过重复选择剩余元素中的最小(或最大)元素进行排序。
- 冒泡排序:通过重复遍历待排序序列,比较相邻元素并交换位置,直到整个序列有序。
- 插入排序:将每个元素插入到已经排序的序列中的适当位置。
- 快速排序:采用分治法策略,通过选取一个“基准”元素,将待排序序列划分为两个子序列,递归地对子序列进行快速排序。
五、递归算法
递归算法是一种通过函数自己调用自己来解决问题的算法。本章介绍了递归算法的基本概念,包括基本情况(递归的终止条件)和一般情况(递归调用自身解决更小的问题)。递归算法在解决某些问题时非常高效,但也需要注意避免无限递归和栈溢出等问题。
六、几个重要思想
本章还强调了计算机科学中的几个重要思想,包括:
- 信息隐蔽:将模块的实现细节隐藏起来,只对外提供必要的接口。
- 抽象:通过忽略问题的某些细节来简化问题。
- 事物命名:合理的命名可以提高代码的可读性和可维护性。
- 测试:包括白盒测试(基于代码本身)和黑盒测试(基于测试所有输入的可能值),以确保算法的正确性和效率。
七、实践与应用
通过本章的学习,学生应该能够亲自动手实现一些基本的算法,如选择排序、冒泡排序、插入排序和快速排序,并通过二分检索算法来查找有序数组中的元素。这些实践活动不仅加深了学生对算法原理的理解,还提高了他们的编程能力。
总之,计算机科学概论第七版的第七章通过系统地介绍问题求解过程、算法设计基础、复合数据结构、排序算法、递归算法以及几个重要思想,为学生提供了丰富的理论知识和实践技能,为后续的学习和研究奠定了坚实的基础。
教材学习中的问题和解决过程(先问 AI)
(一个模板:我看了这一段文字 (引用文字),有这个问题 (提出问题)。 我查了资料,有这些说法(引用说法),根据我的实践,我得到这些经验(描述自己的经验)。 但是我还是不太懂,我的困惑是(说明困惑)。【或者】我反对作者的观点(提出作者的观点,自己的观点,以及理由)。 )
- 问题1:在进行算法测试时,白盒测试和黑盒测试各自的侧重点是什么?为什么在进行算法验证时需要结合使用这两种测试方法?
- 问题1解决方案:在进行算法测试时,白盒测试和黑盒测试各有其独特的侧重点,这些侧重点共同构成了算法验证过程中不可或缺的两个维度。
白盒测试的侧重点
白盒测试,也被称为结构测试、透明盒测试或逻辑驱动测试,主要侧重于测试算法的内部结构和逻辑。这种测试方法要求测试者了解算法的内部实现细节,包括代码的结构、逻辑路径、条件分支等。白盒测试的目标是确保算法内部的每一个逻辑路径都被执行过,每一个条件分支都被测试过,以此来验证算法的正确性和完整性。
白盒测试的侧重点包括:
- 代码覆盖率:通过设计测试用例,确保算法中的所有代码路径都被执行过,从而提高代码的覆盖率。
- 逻辑正确性:验证算法内部的逻辑判断、条件分支等是否按照预期执行,确保算法的逻辑正确性。
- 边界条件:测试算法在边界条件下的表现,如数组越界、除数为零等,以发现潜在的错误和漏洞。
黑盒测试的侧重点
黑盒测试,也被称为功能测试或行为测试,主要侧重于测试算法的功能性和外部行为。这种测试方法将算法视为一个黑盒子,不关心其内部实现细节,只关注算法的输入和输出是否符合预期。黑盒测试的目标是验证算法是否按照需求规格说明书的要求正确地实现了其功能。
黑盒测试的侧重点包括:
- 功能验证:通过设计测试用例,验证算法是否按照需求规格说明书的要求实现了所有功能。
- 用户体验:从用户的角度出发,测试算法的使用是否方便、直观,是否符合用户的习惯和需求。
- 异常处理:测试算法在异常情况下的表现,如输入非法数据、系统资源不足等,以评估算法的健壮性和稳定性。
为什么需要结合使用这两种测试方法
在进行算法验证时,结合使用白盒测试和黑盒测试方法是非常必要的。这是因为这两种测试方法各有优缺点,且侧重点不同,它们之间可以形成互补关系。
- 互补性:白盒测试能够深入算法内部,发现隐藏的逻辑错误和边界条件问题;而黑盒测试则能够从用户的角度出发,验证算法的功能性和用户体验。结合使用这两种方法,可以更加全面地发现算法中的问题和缺陷。
- 提高测试效率:通过结合使用白盒测试和黑盒测试,可以避免重复测试相同的功能点,从而提高测试效率。例如,在白盒测试中已经覆盖过的逻辑路径,在黑盒测试中就不需要再重复测试。
- 增强测试可信度:结合使用两种测试方法,可以从不同的角度对算法进行验证,从而增强测试的可信度。这有助于确保算法的质量符合预期要求,降低软件发布后的风险。
综上所述,在进行算法验证时,结合使用白盒测试和黑盒测试方法是非常必要的。这不仅可以提高测试效率和可信度,还能够更加全面地发现算法中的问题和缺陷。
- 问题2:在算法设计中,有限性、确定性、输入、输出和可行性这五个特性分别如何体现,并且它们对于算法的正确执行有何重要意义?
- 问题2解决方案:在算法设计中,有限性、确定性、输入、输出和可行性这五个特性是算法的基本属性,它们共同构成了算法设计和分析的基础。以下是这五个特性在算法设计中的具体体现及其对于算法正确执行的重要意义:
有限性
体现:有限性指的是算法必须在有限的步骤内结束,不能出现无限循环。这意味着算法的每一步操作都必须在有限的时间内完成,且整个算法的执行步骤是有限的。
意义:有限性保证了算法能够在一定时间内完成,避免了死循环或无尽的等待状态。这对于算法的实际应用至关重要,因为一个无法在有限时间内完成的算法是不可行的。
确定性
体现:确定性指的是算法的每一步操作都必须有明确的定义,不能存在模糊或不确定的操作。每个操作步骤都应该能够被精确地执行,且对于相同的输入,算法应该总是产生相同的输出。
意义:确定性保证了算法的可预测性和一致性。它使得算法的行为是可预见的,从而能够可靠地解决问题。没有确定性的算法可能会导致不可预料的结果,甚至可能导致系统崩溃。
输入
体现:输入指的是算法在执行过程中需要的初始数据或条件。一个算法可以有零个或多个输入,这些输入可以是各种类型的数据,如数字、字符串、数组等。
意义:输入是算法执行的基础,它提供了算法所需的初始条件或数据。合理的输入设计能够确保算法在各种可能的初始条件下都能正确执行。
输出
体现:输出指的是算法执行完成后产生的结果或数据。一个算法通常会有一个或多个输出,这些输出可以是计算结果、状态信息或其他形式的数据。
意义:输出是算法执行的最终目的,它反映了算法对输入数据的处理结果。合理的输出设计能够确保算法的结果是可理解和可利用的,从而满足用户的需求。
可行性
体现:可行性指的是算法的每一步操作都必须是可行的,即每一步都能够通过有限次数的操作完成。这意味着算法中的每一个操作步骤都应该能够在实际的计算环境中执行。
意义:可行性保证了算法的实际可执行性。一个不可行的算法是没有任何实际应用价值的。只有当算法的每一步操作都是可行的,算法才能够在实际的计算环境中得到应用。
总结
有限性、确定性、输入、输出和可行性这五个特性共同构成了算法设计和分析的基础。它们对于算法的正确执行具有重要意义:
- 有限性:保证算法能够在有限时间内完成,避免了死循环或无尽的等待状态。
- 确定性:保证算法的行为是可预见的,从而能够可靠地解决问题。
- 输入:提供了算法执行所需的初始条件或数据。
- 输出:反映了算法对输入数据的处理结果,满足用户的需求。
- 可行性:保证了算法的实际可执行性,使得算法能够在实际的计算环境中得到应用。
这些特性共同确保了算法的正确性、有效性和实用性,从而为算法的实际应用提供了保障。
- ...
基于AI的学习
我正在·学习函数的定义知识点请你以苏格拉底的方式对我进行提问,一次一个问题
Kimi(Moonshot-V1-8k)
很好,让我们开始苏格拉底式的对话。首先,我问你:
你如何定义“函数”这个概念?
在c语言中函数是构成程序的基本模块
Kimi(Moonshot-V1-8k)
明白了,你提到函数是C语言程序的基本模块。那么,我的下一个问题:
这些基本模块——函数,它们在程序中扮演什么角色?
关键性角色
Kimi(Moonshot-V1-8k)
确实,函数在程序中扮演着关键性的角色。那么,让我们进一步探讨:
你能否详细说明一下,为什么说函数是程序中的关键性角色?它们具体承担了哪些关键任务?
函数是编程中实现代码组织、重用、抽象、和维护党的关键工具
Kimi(Moonshot-V1-8k)
非常好,你已经抓住了函数在编程中的核心作用。现在,让我们深入探讨这些关键点:
你如何理解函数帮助实现代码的“组织”?它们是如何帮助程序员将复杂的程序分解为更小、更易管理的部分的?
逻辑分组,减少复杂性,提高可读性,促进代码复用等
Kimi(Moonshot-V1-8k)
你的回答很到位,函数确实通过逻辑分组来减少复杂性、提高可读性,并促进代码复用。现在,让我们继续深入:
你能给我一个具体的例子来说明函数是如何通过逻辑分组来减少复杂性的吗?比如在处理一个特定问题时,你会如何将问题分解为函数?
请你说说
Kimi(Moonshot-V1-8k)
当然,让我们以一个简单的例子来说明这一点:
假设我们要编写一个程序,用于计算一个班级中所有学生的总分和平均分。这个任务可以分解为以下几个步骤:
输入学生成绩:我们可以创建一个函数,用于从用户那里获取每个学生的成绩。
计算总分:我们可以创建另一个函数,用于累加所有学生的成绩,计算总分。
计算平均分:基于总分和学生人数,我们可以创建一个函数来计算平均分。
输出结果:最后,我们可以创建一个函数来输出总分和平均分。
下面是这些步骤对应的C语言函数示例:
C
复制
include <stdio.h>
// 函数1: 输入学生成绩
void inputScores(int scores[], int n) {
for (int i = 0; i < n; i++) {
printf("Enter score for student %d: ", i + 1);
scanf("%d", &scores[i]);
}
}
// 函数2: 计算总分
int calculateTotal(int scores[], int n) {
int total = 0;
for (int i = 0; i < n; i++) {
total += scores[i];
}
return total;
}
// 函数3: 计算平均分
float calculateAverage(int scores[], int n) {
int total = calculateTotal(scores, n);
return (float)total / n;
}
// 函数4: 输出结果
void outputResults(int total, float average) {
printf("Total score: %d\n", total);
printf("Average score: %.2f\n", average);
}
int main() {
int scores[5]; // 假设有5个学生
inputScores(scores, 5);
int total = calculateTotal(scores, 5);
float average = calculateAverage(scores, 5);
outputResults(total, average);
return 0;
}
在这个例子中:
inputScores函数负责输入学生成绩。
calculateTotal函数负责计算总分。
calculateAverage函数负责计算平均分。
outputResults函数负责输出结果。
通过将任务分解为这些独立的函数,我们实现了逻辑分组,每个函数负责一个明确的任务。这样做有以下好处:
减少复杂性:每个函数只关注一个具体的任务,降低了程序的复杂性。
提高可读性:每个函数都有明确的目的和功能,使得代码更易于理解和维护。
促进代码复用:这些函数可以被重用于其他类似的程序,提高了代码的复用性。
通过这种方式,函数帮助我们将复杂的程序分解为更小、更易管理的部分,提高了代码的组织性和可维护性。
代码调试中的问题和解决过程
- 问题1:scanf函数在读取字符时会读取换行符,这会导致输入问题。
- 问题1解决方案:使用getchar()来清除换行符。
- 问题2:swap函数的设计是错误的,因为它试图通过值传递来交换两个整数,但实际上并不会影响调用者传递的参数。
- 问题2解决方案:改为通过指针传递参数,这样可以在函数内部直接交换两个变量的值。
- ...
代码托管
(statistics.sh脚本的运行结果截图)
上周考试错题总结
- 错题1及原因,理解情况:1.捕鱼和分鱼。ABCDE五人在某天夜里合伙去捕鱼,到第二天凌晨时都疲惫不堪,于是各自找地方睡觉。日上三竿,A第一个醒来,将鱼分为5份,把多余的1条鱼扔掉,拿走自己的1份。B第二个本来,也将鱼分5份,把多余的1条鱼扔掉,拿走自己的1份。CDE依次醒来,也按同样的方法拿鱼。问他们合伙至少捕了多少条鱼?
原因:对于核心算法机制没有掌握,导致对于程序的编写出现较大的阻碍 - 错题2及原因,理解情况如果整数A的全部因子(包括1,不包括A本身)之和等于B;且整数B的全部因子(包括1,不包括B本身)之和等于A,则A和B称为亲密数。求10000以内的亲密数。
原因:不知道如何进行亲密数的计算 - ...
其他(感悟、思考等,可选)
钱路漫漫其修远兮,吾将上下而求索
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 300/500 | 2/4 | 18/38 | |
第三周 | 500/1000 | 3/7 | 22/60 | |
第四周 | 600/1300 | 2/9 | 30/90 | |
第五周 | 700/900 | 2/2 | 20/20 | |
第六周 | 800/1000 | 2/4 | 18/38 | |
第七周 | 900/1000 | 3/7 | 22/60 | |
第八周 | 1000/1300 | 2/9 | 30/90 |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
-
计划学习时间:XX小时
-
实际学习时间:XX小时
-
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)
参考资料
- 《计算机科学概论(第七版)》
- ...