分数的加减法——C语言初学者代码中的常见错误与瑕疵(12)
前文链接:分数的加减法——C语言初学者代码中的常见错误与瑕疵(11)
重构
题目的修正
我抛弃了原题中“其中a, b, c, d是一个0-9的整数”这样的前提条件,因为这种限制毫无必要。只假设a, b, c, d是十进制整数形式的字符序列。
我也不清楚这种题目应该如何结束输入。下面的代码假设在没有正确输入完整的运算式时结束。
数据结构
typedef struct { int numer ; //分子 int denom ; //分母 } frac_t ;//分数类型
数据
一共需要三个变量,两个记录分数,一个记录运算符。
#include <stdio.h> int main( void ) { frac_t frc1 , frc2 ;//两个操作数 char op ; //运算符 return 0; }
总体结构
#define FAIL 0 int main( void ) { frac_t frc1 , frc2 ;//两个分数 char op ; //运算符 while ( input_exp( &frc1 , &op , &frc2 ) != FAIL )//输入算式 { //计算,输出 } return 0; }
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 ) != 2 ) return FAIL ; if ( scanf(" %c" , p_o ) != 1 )//if ( scanf(" %c " , p_o ) != 1 ) return FAIL ; switch ( * p_o ) { default : return FAIL ;//不是加、减法 case '+': case '-': ; } if ( input_frac( p_f2 ) != 2 ) return FAIL ; return !FAIL ; } int input_frac( frac_t * p_f ) { return scanf("%d / %d" , &p_f->numer , &p_f->denom ); }
//计算,输出部分
首先排除无意义的输入
if ( frc1.denom == 0 || frc2.denom == 0 ) //无意义的输入 { 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 作者:薛非 出处:http://www.cnblogs.com/pmer/ “C语言初学者代码中的常见错误与瑕疵”系列博文 */ #include <stdio.h> #include <stdlib.h> 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 == 0 || frc2.denom == 0 ) //无意义的输入 { puts( "分数无意义" ); continue ; } switch ( op ) { case '-':frc2.numer = - frc2.numer ;//把减法化为加法 case '+':add_to( &frc1 , &frc2 ); //计算结果放在frc1中 break ; } output( frc1 ); putchar( '\n' ); } return 0; } void output( frac_t fr ) { if ( fr.numer < 0 ) { putchar( '-' ); fr.numer = - fr.numer ; } if ( fr.denom == 1 ) { 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) == 0 ? 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 ) != 2 ) return FAIL ; if ( scanf(" %c" , p_o ) != 1 )//if ( scanf( " %c " , p_o ) != 1 ) return FAIL ; switch ( * p_o ) { default : return FAIL ;//不是加、减法 case '+': case '-': ; } if ( input_frac( p_f2 ) != 2 ) return FAIL ; return !FAIL ; }