重构
题目的修正
我抛弃了原题中“其中a, b, c, d是一个0-9的整数”这样的前提条件,因为这种限制毫无必要。只假设a, b, c, d是十进制整数形式的字符序列。
我也不清楚这种题目应该如何结束输入。下面的代码假设在没有正确输入完整的运算式时结束。
数据结构
typedef
struct
{
int numer ; //分子
int denom ; //分母
}
frac_t ;//分数类型
数据
一共需要三个变量,两个记录分数,一个记录运算符。
#include
int main( void )
{
frac_t frc1 , frc2 ;//两个操作数
char op ; //运算符
return ;
}
总体结构
#define FAIL 0
int main( void )
{
frac_t frc1 , frc2 ;//两个分数
char op ; //运算符
while ( input_exp( &frc1 , &op , &frc2 ) != FAIL )//输入算式
{
//计算,输出
}
return ;
}
input_exp()的实现
int input_exp( frac_t * , char * , frac_t * );
int input_frac( frac_t * );
int input_exp( frac_t * p_f1 , char * p_o , frac_t * p_f2 )
{
if ( input_frac( p_f1 ) != )
return FAIL ;
if ( scanf(" %c" , p_o ) != )//if ( scanf(" %c " , p_o ) != )
return FAIL ;
switch ( * p_o )
{
default : return FAIL ;//不是加、减法
case '+':
case '-':
;
}
if ( input_frac( p_f2 ) != )
return FAIL ;
return !FAIL ;
}
int input_frac( frac_t * p_f )
{
return scanf("%d / %d" , &p_f->numer , &p_f->denom );
}
//计算,输出部分
首先排除无意义的输入
if ( frc1.denom == || frc2.denom == ) //无意义的输入
{
puts( "分数无意义" );
continue ;
}
把减法变为加法
switch ( op )
{
case '-':frc2.numer = - frc2.numer ;//把减法化为加法
case '+':add_to( &frc1 , &frc2 ); //计算结果放在frc1中
break ;
}
最后输出结果
output( frc1 );
putchar( '\n' );
完整的代码:
/*
分数的加减法
编写一个C程序,实现两个分数的加减法
输入:输入包含多行数据
每行数据的格式是 a/boc/d 。
其中a, b, c, d为十进制整数,o是运算符"+"或者"-"。
输出:对于输入数据的每一行输出两个分数的运算结果。
注意结果应符合书写习惯,没有多余的符号、分子、分母,并且化简至最简分数
样例输入:
1/8+3/8
1/4-1/2
1/3-1/3
输出:
1/2
-1/4
0
作者:薛非
出处:/pmer/ “C语言初学者代码中的常见错误与瑕疵”系列博文
*/
#include
#include
typedef
struct
{
int numer ; //分子
int denom ; //分母
}
frac_t ;//分数类型
#define FAIL 0
int input_exp( frac_t * , char * , frac_t * );
int input_frac( frac_t * );
void add_to( frac_t * , frac_t const * );
int find_lcm( int , int );
int find_gcd( int , int );
void reduce( frac_t * );
void output( frac_t );
int main( void )
{
frac_t frc1 , frc2 ;//两个分数
char op ; //运算符
while ( input_exp( &frc1 , &op , &frc2 ) != FAIL )//输入算式
{
//计算,输出
if ( frc1.denom == || frc2.denom == ) //无意义的输入
{
puts( "分数无意义" );
continue ;
}
switch ( op )
{
case '-':frc2.numer = - frc2.numer ;//把减法化为加法
case '+':add_to( &frc1 , &frc2 ); //计算结果放在frc1中
break ;
}
output( frc1 );
putchar( '\n' );
}
return ;
}
void output( frac_t fr )
{
if ( fr.numer < )
{
putchar( '-' );
fr.numer = - fr.numer ;
}
if ( fr.denom == )
{
printf( "%d" , fr.numer );
return ;
}
printf( "%d/%d" , fr.numer , fr.denom );
}
void reduce( frac_t * p_f )
{
int gcd = find_gcd( abs( p_f->numer ) , abs( p_f->denom ) ) ;
p_f->denom /= gcd ;
p_f->numer /= gcd ;
}
int find_gcd( int m , int n )
{
int t ;
return (t = m % n) == ? n : find_gcd( n , t );
}
int find_lcm( int m , int n )
{
return m / find_gcd( m , n ) * n ;
}
void add_to( frac_t * p_f1 , frac_t const * p_f2 )
{
int lcm = find_lcm( abs( p_f1->denom ) , abs( p_f2->denom ) );
p_f1->numer = lcm / p_f1->denom * p_f1->numer
+ lcm / p_f2->denom * p_f2->numer ;
p_f1->denom = lcm ; //分母总是正的
reduce( p_f1 ); //约分
}
int input_frac( frac_t * p_f )
{
return scanf( "%d / %d" , &p_f->numer , &p_f->denom );
}
int input_exp( frac_t * p_f1 , char * p_o , frac_t * p_f2 )
{
if ( input_frac( p_f1 ) != )
return FAIL ;
if ( scanf(" %c" , p_o ) != )//if ( scanf( " %c " , p_o ) != )
return FAIL ;
switch ( * p_o )
{
default : return FAIL ;//不是加、减法
case '+':
case '-':
;
}
if ( input_frac( p_f2 ) != )
return FAIL ;
return !FAIL ;
}
一个超复杂的间接递归——C语言初学者代码中的常见错误与瑕疵(6)
问题: 问题出处见 C语言初学者代码中的常见错误与瑕疵(5). 在该文的最后,曾提到完成的代码还有进一步改进的余地.本文完成了这个改进.所以本文讨论的并不是初学者代码中的常见错误与瑕疵,而是对我自己 ...
C语言初学者代码中的常见错误与瑕疵(5)
问题: 素数 在世博园某信息通信馆中,游客可利用手机等终端参与互动小游戏,与虚拟人物Kr. Kong 进行猜数比赛. 当屏幕出现一个整数X时,若你能比Kr. Kong更快的发出最接近它的素数答案,你将 ...
C语言初学者代码中的常见错误与瑕疵(14)
见:C语言初学者代码中的常见错误与瑕疵(14) 相关链接:/blog/?p=87
C语言初学者代码中的常见错误与瑕疵(9)
题目 字母的个数 现在给你一个由小写字母组成字符串,要你找出字符串中出现次数最多的字母,如果出现次数最多字母有多个那么输出最小的那个. 输入:第一行输入一个正整数T(0
要心中有“数”——C语言初学者代码中的常见错误与瑕疵(8)
在 C语言初学者代码中的常见错误与瑕疵(7) 中,我给出的重构代码中存在BUG.这个BUG是在飞鸟_Asuka网友指出“是不是时间复杂度比较大”,并说他“第一眼看到我就想把它当成一个数学问题来做”之后 ...
C语言初学者代码中的常见错误与瑕疵(7)
问题: 矩形的个数 在一个3*2的矩形中,可以找到6个1*1的矩形,4个2*1的矩形3个1*2的矩形,2个2*2的矩形,2个3*1的矩形和1个3*2的矩形,总共18个矩形.给出A,B,计算可以从中找到 ...
C语言初学者代码中的常见错误与瑕疵(1)
曾在豆瓣上看到过一个小朋友贴出他自己的代码(/group/topic/40293109/),当时随口指点了几句.难得这位小朋友虚心修正.从善如流,不断地改,又 ...
随机推荐
使用XmlDataDocument将数据存储到XML文档
string str = "Data Source=192.168.1.20;Initial Catalog=WebTest;User ID=sa;Password="; SqlC ...
SQL Server中解决死锁
SQL Server中解决死锁的新方法介绍 数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁,通过SQL Server , 现在似乎有了一种新的解决办法. 将下面的 ...
status 状态栏
http://www.pchou.info/ios//08/22/oc-statusbar.html IOS的项目多数会遇到控制状态栏和导航栏的问题,比如隐藏状态栏.控制状态栏的文字颜色等,导 ...
Asp.net默认配置下,Session莫名丢失的原因及解决
默认配置下,Session莫名丢失的原因及解决 我们平时写的程序,里面要用到Session来保存一些跨页面的数据.但是Session会经常无故丢失,上网查查,也没找到原因. ...
fscanf(格式化字符串输入)
fscanf(格式化字符串输入) 相关函数 scanf,sscanf 表头文件 #include 定义函数 int fscanf(FILE * stream ,const ...
简易ORM(基于注解)
这是从我们现有项目做的一定的改进准备做成IDE插件 类似getter和setter的生成 1.定义实体类通过注解说明其表名和字段名(SOURCE类型的注解 不需要运行时使用)@TableName(& ...
轻松理解JavaScript闭包
摘要 闭包机制是JavaScript的重点和难点,本文希望能帮助大家轻松的学习闭包 一.什么是闭包? 闭包就是可以访问另一个函数作用域中变量的函数. 下面列举出常见的闭包实现方式,以例子讲解闭包概念 ...
多线程面试题系列(5):经典线程同步 关键段CS
上一篇提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题.本文首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现机制与原理.关键段CRITIC ...
innerHTML、outerHTML、innerText、outerText的区别及兼容性问题
今天看了很多文章关于innerHTML.outerHTML.innerText.outerText的区别,都是很模糊的一个介绍,所以自己总结下这些区别以及一些重点内容.很多文章在描述这些区别的时候,都 ...
PyQt 5事件和信号
信号槽Signals & slots sld.valueChanged.connect(lcd.display) # 将滚动条的valueChanged信号连接到lcd的display插槽 # ...