本講義では 2 通のレポートで評価します。 レポートは A4 の紙を縦に使い、適宜表紙を付けて提出すること。 またプログラムは C 言語で作成しなさい。
なお、遅れレポートは原則受けとりません。
課題1に引き続き、分数を取り扱う。 分数の電卓を作成することを目指す。 なお構造体として、以下の二つを定義しておく。
typedef struct {
int bunshi;
int bunbo;
} BUNSU;
typedef struct stack {
BUNSU bunsu;
struct stack* next;
}STACK;
なお、bunsu.h や、他に必要な関数は後述する。
ユークリッドの互除法を利用して、BUNSUのポインタを与えて、その分数を約 分する関数 void yakubun(BUNSU* b) を作成しなさい。 ただし、ユークリッドの互除法とは、二つの整数 a>b≥0 より最大公 約数 gcd(a,b) を求める以下のアルゴリズムである。
なお、以下のプログラムと結合してテストを行い、実行結果を示しなさい。
#include <stdio.h>
#include "bunsu.h"
int main(void){
BUNSU b[] ={{3,4},{-6,3},{10,-2},{-2,-30},{0,-10},{0,11},{0,0}};
BUNSU *p;
for(p=b;p->bunbo!=0;p++){
printbunsu(*p);
printf(" -> ");
yakubun(p);
printbunsu(*p);
printf("\n");
}
return 0;
}
3/4 -> 3/4 -6/3 -> -2/1 -10/2 -> -5/1 2/30 -> 1/15 0 -> 0 0 -> 0
二つの BUNSU 型の値 a,b を受け取って、和、積を求める関数 BUNSU add(BUNSU a, BUNSU b), BUNSU mul(BUNSU a, BUNSU b) を両方共作成しなさ い。
そして、以下のテストプログラムと結合して動作が正常であることを示しな さい。
#include <stdio.h>
#include "bunsu.h"
int main(void){
BUNSU b[] ={{3,4},{-6,3},{10,-2},{-2,-30},{0,0}};
BUNSU *p;
BUNSU wa={0,1};
BUNSU seki = {1,1};
for(p=b;p->bunbo!=0;p++){
printbunsu(*p);
printf("\n");
wa = add(wa,*p);
seki = mul(seki,*p);
}
yakubun(&wa);
yakubun(&seki);
printf("wa: ");
printbunsu(wa);
printf("\tseki: ");
printbunsu(seki);
printf("\n");
return 0;
}
3/4 -6/3 -10/2 2/30 wa: -371/60 seki: 1/2
構造体 STACK を利用して、BUNSU のスタックを作成しなさい。 具体的には int empty(void), void push(BUNSU b), BUNSU pop(void) の三 つの関数をすべて実装しなさい。 なお、メモリーが確保できなかったときは、その場で stdlib.h で定義され ている exit(1) を呼び出してプログラムを止めてよい。 そして、以下のテストプログラムと結合して、テスト結果を示しなさい。
#include <stdio.h>
#include "bunsu.h"
int main(void){
int i,j;
BUNSU work;
for(i=1; i<10; i++){
for(j=1; j<10; j++){
work.bunshi=i;
work.bunbo=j;
yakubun(&work);
printbunsu(work);
printf("\t");
push(work);
}
printf("\n");
}
printf("----------\n");
i=0;
while(!empty()){
printbunsu(pop());
printf((++i%9)?"\t":"\n");
}
return 0;
}
1/1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9 2/1 1/1 2/3 1/2 2/5 1/3 2/7 1/4 2/9 3/1 3/2 1/1 3/4 3/5 1/2 3/7 3/8 1/3 4/1 2/1 4/3 1/1 4/5 2/3 4/7 1/2 4/9 5/1 5/2 5/3 5/4 1/1 5/6 5/7 5/8 5/9 6/1 3/1 2/1 3/2 6/5 1/1 6/7 3/4 2/3 7/1 7/2 7/3 7/4 7/5 7/6 1/1 7/8 7/9 8/1 4/1 8/3 2/1 8/5 4/3 8/7 1/1 8/9 9/1 9/2 3/1 9/4 9/5 3/2 9/7 9/8 1/1 ---------- 1/1 9/8 9/7 3/2 9/5 9/4 3/1 9/2 9/1 8/9 1/1 8/7 4/3 8/5 2/1 8/3 4/1 8/1 7/9 7/8 1/1 7/6 7/5 7/4 7/3 7/2 7/1 2/3 3/4 6/7 1/1 6/5 3/2 2/1 3/1 6/1 5/9 5/8 5/7 5/6 1/1 5/4 5/3 5/2 5/1 4/9 1/2 4/7 2/3 4/5 1/1 4/3 2/1 4/1 1/3 3/8 3/7 1/2 3/5 3/4 1/1 3/2 3/1 2/9 1/4 2/7 1/3 2/5 1/2 2/3 1/1 2/1 1/9 1/8 1/7 1/6 1/5 1/4 1/3 1/2 1/1
演算子を最後に置く数式の記法を逆ポーランド記法と言う。 例えば、 ((1 * 2) + 3) は ((1 2 *) 3 +) などと書く。 なお、演算子を最後に置いた式では、最初から読んでいくと、演算子を置い た場所でカッコが閉じられるので、そこで必ず計算を実施してよい。 つまり、これはカッコを全て省略できる。 上記の式は 1 * 2 + 3 = 1 2 * 3 + と書ける(但し、真ん中に演算子を置 く記法と異なり、数と数とを切り離すための空白などの区切り記号が必要)。 一方、 1*(2+3) = 1 2 3 + * と書ける。
この逆ポーランド記法の式を計算するのは、スタックを使うと容易にできる。 これは次の様にすれば良い
さて、文字列のポインター変数のポインターから記述されている分数を
BUNSU として取り出し、ポインターを進める BUNSU toBunsu(char** p),
空白を読み飛ばす関数 voidint skipSpace(char** p),
演算子記号かどうかを判定する int isEnzanshi(char c) を下記の通りとす
るとき、分数、足し算記号、掛け算記号だけからなる文字列中の逆ポーラン
ド記法の式から値を求める関数 BUNSU calc(char *p) を作成しなさい。
そして、以下のテストプログラムと結合して、テスト結果を示しなさい。
#include <stdio.h>
#include <ctype.h>
#include "bunsu.h"
BUNSU toBunsu(char** p){
BUNSU result={0,1};
int sign=1;
if(**p=='-'){
sign*=-1;
(*p)++;
}
while((isdigit(**p)))
result.bunshi=result.bunshi*10+ *((*p)++) -'0';
if(**p=='/'){
result.bunbo=0;
(*p)++;
if(**p=='-'){
sign*=-1;
(*p)++;
}
while((isdigit(**p)))
result.bunbo=result.bunbo*10+ *((*p)++) -'0';
}
result.bunshi*=sign;
return result;
}
int skipSpace(char **p){
while(**p==' ')(*p)++;
return **p!='\0';
}
int isEnzanshi(char c){
return (c=='+')||(c=='*');
}
#include <stdio.h>
#include <stdlib.h>
#include "bunsu.h"
int main(void){
char* input[]={"1/2 1/3 + 3 *",
"-1/3 5/4 2/7 * +",
"-2/3 -4/5 -6/7 * *",
"3/4 5/6 + 7/8 +",
NULL};
char** p;
for(p=input; *p!=NULL; p++){
printf("%s = ",*p);
printbunsu(calc(*p));
printf("\n");
}
return 0;
}
1/2 1/3 + 3 * = 5/2 -1/3 5/4 2/7 * + = 1/42 -2/3 -4/5 -6/7 * * = -16/35 3/4 5/6 + 7/8 + = 59/24
#include <stdio.h>
#include "bunsu.h"
int main(void){
char input[] = "1/-3 -4/5 -2/-5 -6 5/6";
char *p;
p=input;
while(skipSpace(&p)){
printbunsu(toBunsu(&p));
printf("\n");
}
return 0;
}
typedef struct {
int bunshi;
int bunbo;
} BUNSU;
typedef struct stack {
BUNSU bunsu;
struct stack* next;
}STACK;
double bkazu(BUNSU b);
int bsign(BUNSU b);
BUNSU babs(BUNSU b);
void printbunsu(BUNSU b);
void yakubun(BUNSU* b);
BUNSU add(BUNSU a, BUNSU b);
BUNSU mul(BUNSU a, BUNSU b);
int empty(void);
void push(BUNSU b);
BUNSU pop(void);
BUNSU toBunsu(char** p);
int skipSpace(char **p);
int isEnzanshi(char c);
BUNSU calc(char* p);
レポートには自ら作成した関数の説明をし、テストプログラムと結合して実行 した結果を載せること。
解答法として、分割コンパイルによる別ファイルにプログラムを書く方 法と、下記のテストプログラムに関数を追記する方法の二通りがあるがどちら でも良い。 プログラムの解説は、自分で作成した関数について行うこと。 こちらで提供したテストプログラムの説明は基本的には不要である。 但し、自分で作成した関数を説明するのに必要な内容には言及すること。
なお、遅れレポートは教員に直接提出してください。 遅れた方が有利にならない範囲内で採点します。
分数を取り扱う構造体として次を用意した。
typedef struct {
int bunshi;
int bunbo;
} BUNSU;
なお、bunsu.h は後述する。
BUNSU 型を受け取り、分数の値を double 型として返す関数 double bkazu(BUNSU) を 作りなさい。
そして、テストプログラム1と結合して正しく動作していることを確認しなさい。
#include <stdio.h>
#include "bunsu.h"
int main(void){
BUNSU b[] ={{3,4},{-5,3},{1,-2},{-2,-3},{0,-1},{0,1},{0,0}};
BUNSU *p;
for(p=b;p->bunbo!=0;p++){
printf("%f\n",bkazu(*p));
}
return 0;
}
0.750000 -1.666667 -0.500000 0.666667 -0.000000 0.000000
BUNSU 型を受け取り、分数の符号が負ならば -1, 0 ならば 0, 正ならば 1 を 返す関数 int bsign(BUNSU) を作りなさい。
そして、下記のテストプログラム2と結合して、正しく動作することを確かめなさい。
#include <stdio.h>
#include "bunsu.h"
int main(void){
BUNSU b[] ={{3,4},{-5,3},{1,-2},{-2,-3},{0,-1},{0,1},{0,0}};
BUNSU *p;
for(p=b;p->bunbo!=0;p++){
printf("%d\n",bsign(*p));
}
return 0;
}
1 -1 -1 1 0 0
BUNSU 型を受け取り、分子、分母とも正にした絶対値を返す関数 BUNSU babs(BUNSU) を作りなさい。 なお、stdlib.h に定義されている int 型の絶対値を返す関数 int abs(int) を使用してよい。
そして、下記のテストプログラム3と結合して、正しく動作することを確かめなさい。
#include <stdio.h>
#include "bunsu.h"
int main(void){
BUNSU a;
BUNSU b[] ={{3,4},{-5,3},{1,-2},{-2,-3},{0,-1},{0,1},{0,0}};
BUNSU *p;
for(p=b;p->bunbo!=0;p++){
a= babs(*p);
printf("%d %d\n",a.bunshi, a.bunbo);
}
return 0;
}
3 4 5 3 1 2 2 3 0 1 0 1
BUNSU 型を受け取り、値が 0 ならば 0、そうでなければ、負の場合はマイナ ス記号"-"を先頭につけ、分子/分母 という形式で出力する 関数 void printbunsu(BUNSU) を作りなさい。 なお、課題1-1 から 1-3 で定義した関数を呼び出してよい。
そして、下記のテストプログラム4と結合して、正しく動作することを確かめなさい。
#include <stdio.h>
#include "bunsu.h"
int main(void){
BUNSU b[] ={{3,4},{-5,3},{1,-2},{-2,-3},{0,-1},{0,1},{0,0}};
BUNSU *p;
for(p=b;p->bunbo!=0;p++){
printbunsu(*p);
printf("\n");
}
return 0;
}
3/4 -5/3 -1/2 2/3 0 0
bunsu.h は次のように上記の構造体と作るべき関数のすべてのプロトタイプ 宣言が入っているファイルとする。
typedef struct {
int bunshi;
int bunbo;
} BUNSU;
double bkazu(BUNSU b);
int bsign(BUNSU b);
BUNSU babs(BUNSU b);
void printbunsu(BUNSU b);