又是时候了。 一个新的学期即将开始,全国各地的CIS学生都开始学习课程。 在学习有效地编码分配时尝试破译语法可能是压倒性的经历。 我发现有很多做法会使刚开始编程的学生因作业而受苦。 我将在本文中概述这些问题。
我将使用第一学期课程的简短典型编程作业来向学生展示如何分析问题并将其分解为可管理的部分。 该分配将一个温度转换为开尔文,摄氏和华氏温度。
分析101除了不检查返回码外,我看到的最大错误是学生开始编写代码而没有对问题进行任何分析。 他们将编写一些代码,确定其工作原理,然后编写更多代码。 很快,一个整体程序就可以编译了,他们想知道为什么会有那么多错误。 他们遵循这封信的指导方针。 他们已经从教师过去20个学期一直在使用的工作表中复制了代码。 没关系,该示例与当前分配无关。 或在某些部分故意含糊不清,以使学生思考任务。 习惯了模糊的规范,它们是开发中的一种生活方式。 在实际项目中,开发人员将与客户澄清任何歧义。 在学术界,客户是您的指导者。 请他/她澄清似乎不清楚的任何事情。 提出问题没有耻辱。 假设是所有无效程序的源泉。
避免编写整体程序的痛苦,并在编码之前分析问题。 首先确定预期的输出。 这是最后的比赛。 这就是我们正在努力的方向。 接下来,确定完成任务所需的数据。 然后,如何输入数据; 从存储读取,直接用户输入还是可以对其进行硬编码? 接下来,确定从原始数据到预期输出将执行哪些任务。 最后,将任务分解为完成任务一部分的小功能/方法。
终结游戏规范说将一种温度标度转换为另一种。 它没有说如何输入数据,也没有说我们是否转换为所有其他比例。 我们稍后将对此进行澄清,现在最终的游戏将是这样:
- 给定开氏温度,可以将其转换为华氏和摄氏
- 在摄氏温度下,可以转换为法伦海特和开尔文
- 在华氏温度下,可以转换为摄氏和开氏温度
这些转换的每次计算都是众所周知的,因此在此不再赘述。 如果您需要数学上的帮助,请在Wiki上查找Celsius。
数据与它有什么关系首先,我们需要温度,然后需要转换的刻度,然后需要转换的刻度。 这些方面的规范尚不明确,因此我们向教师提出了一系列问题:
- 数据来自哪里,用户,存储或硬编码值?
- 输入的格式是什么:247.67K或247.67 K等...?
- 我们问转换为哪个比例还是仅转换为所有其他比例?
对于此特定任务,讲师需要用户输入。 应检查输入的正确性,开尔文是否为负值以及绝对值是否小于零。 输入温度的值将采用247.67 K,34.56 C和56.56 F的形式。可以将温度和标度的数据输入分开。 字母需要以大写形式输出以匹配标准约定,但是用户应该可以自由输入小写字母。 不要像开尔文(Kelvins)一样进行相同的比例转换。
我们如何从这里到达那里我们需要完成的任务是:
- 获取用户输入的温度和刻度
- 验证秤输入
- 验证温度输入
- 获取要转换为Scale的用户条目
- 验证秤输入
- 转换温度
- 显示结果
这是新手开发人员犯的另一个错误。 输入和输出格式是已知的,转换是已知的,让我们开始编码。 通常,这种方法最终将所有代码填充到程序的Main部分中,而没有任务细分,因此除了剪切和粘贴外,没有重用。 测试变得令人头疼,因为开发人员必须仔细检查所有这些代码才能识别出问题区域。 变化意味着必须对所有路径进行测试,以确保没有损坏。 此时最好的座右铭是“小代码,小测试”。 这意味着编写紧凑的完全自包含的功能或模块,它们可以完成一项任务,先做好任务,然后继续前进。
分解,分解请注意,从上一节的任务中可以看到,对Scale条目进行了两次验证。 在单片程序中,结果是重复的代码。 使它成为仅验证正确的“比例”条目C,F和K的函数。只需花费很少的工作,就可以为此创建可重复使用的通用函数。 一种解决方案是传入Scale条目和一串有效条目。 另一个解决方案可能还包括数据输入函数的返回码作为参数,以确保正确输入我们期望的所有数据。
接下来解决温度验证。 制作一个功能来检查不同刻度的有效温度。 一种解决方案通过了温度和标度,并使用一些简单的控制语句即可完成任务。 添加返回码也可以是一种选择。
现在用于数据输入。 程序员可能决定编写两个函数,一个用于温度和刻度,另一个仅用于刻度。 这是可以接受的解决方案,但是更通用的解决方案可能需要一个以上的参数,并且能够在单个函数中获得“温度”或“标度”或“两者”。 该函数应返回完成的成功条目数。
转换过程一开始可能看起来很艰巨,程序员可能认为需要创建六个不同的函数。 经过仔细检查,虽然只需要四个:
- KelvinToCelsius-取开氏温度并返回摄氏度
- CelsiusToKelvin-摄氏温度并返回Kelvin
- CelsiusToFarenheit-摄氏温度并返回Farenheit
- FarenheitToCelsius-取一个Farenheit温度并返回摄氏温度
开尔文函数非常琐碎,以至于它们可以是内联计算,只需加或减273.15,从而将转换函数减少到只有两个。
这只是一个测试初学者遇到的最后一个问题不是测试每个任务。 很多时候,程序员会集成某些功能而不进行测试,而只是看到程序中断。 在将功能集成到更大的程序之前,请使用简单的测试Main来检查功能。 单元测试框架也很棒。
分解的曙光我们的程序具有以下功能:
- 功能主要功能
- GetTemperatureScale(超出温度,超出比例,其中)为整数
- 函数IsValidTemperatureScale(在温度,小数位数中)为布尔值
- 函数ConvertTemperatureScale(In TemperatureFrom,In ScaleFrom,In TemperatureTo,In ScaleTo)为实数
- 函数CelsiusToFarenheit(以摄氏度为单位)为实数
- 函数FarenheitToCelsius(在Farenheit中)为实数
- 函数ShowResult(在Temp1中,在Scale1中,在Temp2中,在Scale2中)
注意:上面的伪代码使用Out进行引用传递,使用In进行值传递。 As子句是返回类型。 我们还决定内联开尔文转换。
摘要在上面的温度示例中,我们确定了所需的结果,确定了数据,创建了从原始数据到结果的任务。 在开始编写解决方案代码之前,我们还通过询问一些简单的问题与客户达成了一些模棱两可的要求。 识别的任务以这样的方式编码,使得在另一个程序中的重用变得简单。 以较小的增量进行测试意味着随着我们从一个任务到另一个任务的进行,更少的代码需要担心。 从长远来看,已完成的程序更易于跟踪和调试。
解决方案
///
/// \file main.c
/// \brief Main module for Temperature program
///
/// \author John `Ghost' Wicks
/// \version 1.0.0
/// \date 2008-08-08
///
///
#include <stdio.h>
#include <ctype.h>
int getTemperatureScale( double* temp, char* scale, int which );
int isValidTemperatureScale( double temp, char scale, int which, int howMany );
double convertTemperatureScale( double tempFrom, char scaleFrom, char scaleTo );
double celsiusToFarenheit( double temp );
double farenheitToCelsius( double temp );
void showResults( double tempFrom, char scaleFrom, double tempTo, char scaleTo );
/
/// \fn main(int argc, char** argv)
/// \brief Main module for Temperature conversion program.
///
/// \param argc - integer number of arguments passed from system to program
/// \param argv - pointer to array of characters of arguments passed to program from system
///
/// \return integer value for success or failure
/
int main( int argc, char* argv[] )
{
int retVal = 0;
int r = 0;
double tempFrom = 0.0, tempTo = 0.0;
char scaleFrom, scaleTo;
do{
r = getTemperatureScale( &tempFrom, &scaleFrom, 2 );
}while(!isValidTemperatureScale( tempFrom, scaleFrom, 2, r ));
do{
r = getTemperatureScale( &tempTo, &scaleTo, 0 );
}while(!isValidTemperatureScale( tempTo, scaleTo, 0, r ));
showResults( tempFrom, scaleFrom, tempTo, scaleTo );
return retVal;
}
/
/// \fn getTemperatureScale( double* temp, char* scale, int which )
/// \brief Gets data from user regarding temperature
///
/// \param temp - pointer to storage for temperature value
/// \param scale - pointer to storage for temperature scale
/// \param which - allows multiuse of function for just scale (0) or temperature (1) or both (2)
///
/// \return integer returned from scanf
/
int getTemperatureScale( double* temp, char* scale, int which )
{
int retVal = 0;
switch( which ){
case 2:
printf("Enter the Temperature and Scale (CFK) (Example 270.15 K): ");
retVal = scanf_s(" %lf %c^%*", temp, scale);
break;
case 1:
printf("Enter Temperature : ");
retVal = scanf_s(" %lf^%*", temp);
break;
default:
printf("Enter Scale : ");
retVal = scanf_s(" %c^%*", scale);
break;
}
return retVal;
}
/
/// \fn isValidTemperatureScale( double temp, char scale, int which, int howMany )
/// \brief Checks if scale or temperature are invalid
///
/// \param temp - temperature value to check
/// \param scale - scale to check
/// \param which - allows multiuse of function for just scale (0) or temperature (1) or both (2)
/// \param howMany - check to see if all expected values were correctly entered from scanf function
///
/// \return integer value for success (1) or failure (0) of validation check
/
int isValidTemperatureScale( double temp, char scale, int which, int howMany )
{
int retVal = 0;
switch( which ){
case 2:
if( howMany == which ){
switch( toupper(scale) ){
case 'C':
if( temp >= -273.15 ) retVal = 1;
break;
case 'F':
if( temp >= -459.67 ) retVal = 1;
break;
default: // Kelvin
if( temp >= 0 ) retVal = 1;
break;
}
}
break;
case 1:
//Can't check Temperature without knowing scale
break;
default:
if( howMany == 1 ){
switch( toupper(scale) ){
case 'C':
case 'F':
case 'K':
retVal = 1;
break;
default:
break;
}
}
break;
}
return retVal;
}
/
/// \fn convertTemperatureScale( double tempFrom, char scaleFrom, char scaleTo )
/// \brief Converts from one temperature scale to another
///
/// \param tempFrom - temperature value to convert from
/// \param scaleFrom - scale to convert from
/// \param scaleTo - scale to convert to
///
/// \return double value of temperature converted to
/
double convertTemperatureScale( double tempFrom, char scaleFrom, char scaleTo )
{
double retVal = 0.0;
switch( toupper(scaleTo) ){
case 'C':
if( toupper(scaleFrom) == 'K') retVal = tempFrom - 273.15;
else
retVal = farenheitToCelsius( tempFrom );
break;
case 'F':
if( toupper(scaleFrom) == 'K') retVal = celsiusToFarenheit(tempFrom - 273.15);
else
retVal = celsiusToFarenheit( tempFrom );
break;
case 'K':
if( toupper(scaleFrom) == 'C' ) retVal = tempFrom + 273.15;
else
retVal = farenheitToCelsius( tempFrom ) + 273.15;
break;
default:
break;
}
return retVal;
}
/
/// \fn celsiusToFarenheit( double temp )
/// \brief Converts from Celsius temperature scale to Farenheit scale
///
/// \param temp - Celsius value to convert
///
/// \return double value in Farenheit scale temperature
/
double celsiusToFarenheit( double temp )
{
double retVal = 0.0;
retVal = 9/5 * temp+32;
return retVal;
}
/
/// \fn farenheitToCelsius( double temp )
/// \brief Converts from Farenheit temperature scale to Celsius scale
///
/// \param temp Farenheit value to convert
///
/// \return double value of Celsius scale temperature
/
double farenheitToCelsius( double temp )
{
double retVal = 0.0;
retVal = (temp - 32) * 5/9;
return retVal;
}
/
/// \fn showResults( double tempFrom, char scaleFrom, double tempTo, char scaleTo )
/// \brief Shows the converted temperature value
///
/// \param tempFrom - temperature value converted from
/// \param scaleFrom - scale to converted from
/// \param tempTo - temperature value converted to
/// \param scaleTo - scale to converted to
///
/// \return none
/
void showResults( double tempFrom, char scaleFrom, double tempTo, char scaleTo )
{
printf("Original Temperature: %.2lf %c\n", tempFrom, toupper(scaleFrom));
printf("Scale To: %c\n", toupper(scaleTo));
if( toupper(scaleFrom) != toupper(scaleTo) ){
printf("Converting...\n");
tempTo = convertTemperatureScale( tempFrom, scaleFrom, scaleTo );
printf("Converted Temperature: %.2lf %c\n", tempTo, toupper(scaleTo));
}
else{
printf("No conversion needed...\n");
}
}
From: https://bytes.com/topic/c/insights/830218-zen-art-requirements-analysis