レポート見本 1

データ構造とアルゴリズム I レポート課題

題名
文字の頻度
学籍番号
08nc999
氏名
坂本直志

課題1

int 型のグローバルな配列 hindo[95] が定義されているとします。但し、 hindo[k] は文字コード k+32(十進法)の文字の出現回数を表すこととします。 以下の問いに答えなさい。

課題 1-1

頻度を出力する void dispHindo(void) を作成しなさい。書式は一行ずつ 「'文字': 頻度」となるようにしなさい。但し、頻度が 0 の文字は出力をしない ようにしてください。そして、以下のプログラムと結合し、出力結果が以下の 出力例と同じになることを確認しなさい。


#include <stdio.h>
int hindo[95]={0,1,0,2,0,3};
void dispHindo(void){
/* ここにプログラムを書く */
}
int main(void){
  dispHindo();
  return 0;
}

出力例

'!': 1
'#': 2
'%': 3

課題 1-2

文字配列のポインタを受け取り、文字出現の頻度を計算する void countHindo(char*) を作成しなさい。なお、文字列の中に改行記号などのコン トロールコードが含まれることも想定し、文字コードが 32 から 126 以内で あるときだけ頻度を数えるようにしなさい。そして、下記のテストプログラム と結合して、結果を報告しなさい。

プログラムA


#include <stdio.h>
int hindo[95]={0};
void dispHindo(void){
  /* 課題1-1 のプログラム */
}
void countHindo(char* str){
  /* ここにプログラムを書く */
}
int main(void){
  countHindo("This is a pin.");
  dispHindo();
  return 0;
}

課題 1-3

開いたファイルのファイルハンドルをもらい、ファイル中の各文字の頻度を計 算する void fCountHindo(FILE*) を作成しなさい。そして、作成したプログ ラムのファイル名を kadai13.c とした時、以下のプログラムと結合して結果 を求めなさい。なお、これも入力のチェックは行ってください。

テストプログラム


#include <stdio.h>
int hindo[95]={0};
void dispHindo(void){
  /* 課題 1-1 のプログラム */
}
void fCountHindo(FILE* input){
  /* この部分を作成する */
}
int main(void){
  FILE* input;
  if((input=fopen("kadai13.c","r"))==NULL){
    fprintf(stderr,"Can't open the file.\n");
    return 1;
  }
  fCountHindo(input);
  dispHindo();
  fclose(input);
  return 0;
}

課題1-4

課題1-3 で作成した void fCountHindo(FILE*) に対して、英小文字を大文字 として数える void fCountHindoIgnoreCase(FILE *) を作りなさい。これも入 力のチェックが必要です。作成したプログラムに対して、課題 1-3 と同じ kadai13.c を読ませて出力を求めなさい。

テストプログラム


#include <stdio.h>
int hindo[95]={0};
void dispHindo(void){
  /* 課題 1-1 のプログラム */
}
void fCountHindoIgnoreCase(FILE* input){
  /* この部分を作成する */
}
int main(void){
  FILE* input;
  if((input=fopen("kadai13.c","r"))==NULL){
    fprintf(stderr,"Can't open the file.\n");
    return 1;
  }
  fCountHindoIgnoreCase(input);
  dispHindo();
  fclose(input);
  return 0;
}

基礎知識

ポインタ

C 言語では変数の格納されている番地を扱うことができる。 変数名の前に & を付けると、その変数の番地を意味する。 また、変数の番地を取り扱う変数も用意されている。 そのような変数をポインタと言う。 ポインタは格納される値の型で区別される。 int の値が入る変数のポインタは int 型のポインタなどと言う。 そして int 型のポインタの宣言は int *ポインタ名 のよ うにする。 ポインタに対して、その番地に入っている値は*ポインタ名で表す。 以下に簡単な例を示す。


int x, *y;
x=1;
y=&x;
printf("%d\n",*y); /* 1 が表示される */

C 言語では配列変数はポインタにより実装されている。 配列 a[0], a[1] に対して、 a は a[0] の番地を示している。 つまり a と &a[0] は同じになる。 また、ポインタに対しても [値] とすると配列と同じように要素にアクセスで きる。 さらに、ポインタに 1 を足すとメモリの番地として 1 増えるのではなく、配列 として次の要素を指すようになる。 つまり次のような操作が許される。


int a[2];
int *x;
a[0]=5;
a[1]=6;
x=a;
printf("%d\n",*x); /* 5 が表示される */
printf("%d\n",x[1]); /* 6 が表示される */
x+=1;
printf("%d\n",*x); /* 6 が表示される */

つまり配列の宣言とポインタの宣言は領域を確保するかしないかだけで、基本 的には同じような意味となっている。

関数

C 言語ではプログラムを分割、再利用するために関数と言う概念がある。 関数は関数名(引数1,引数2, ...)という形で呼び出すと値を返す。 返してきた値を変数に代入するには変数=関数名(引数1,引数2, ...)という構文になる。 C 言語でプログラムを実行する時に呼び出されるのが main 関数である。

一方、関数の内部で定義された変数はローカル変数と呼び、他の関数か らアクセスできない。 関数の外側でも変数の定義ができる。 外側で定義した変数をグローバル変数と呼ぶ。 グローバル変数にはあらゆる関数からアクセスができるので、関数の情報を受 け渡すのに使用できるが、受渡しがうまく行ってない場合に原因を突き止める のが難しい。

C 言語の関数は値呼び出しである。 つまり関数の引数は関数側にとっては値が代入されているローカル変数となる。 したがって、関数の内部で値を変更しても、呼び出し側の引数の値を変更すること はできない。 そのため、変数の内容を関数で変更させるには、変数の番地を関数に与え、関数 内部ではポインタにより変数にアクセスする必要がある。

番兵

番兵とは複数のデータを扱う際に、データの終りにさらにデータの終りを示す データを与えるものである。 番兵を用いることによりデータの個数を意識しなくても複数のデータを処理す ることができるようになる。 配列変数を関数で処理する場合も、番兵を与えておけばデータの個数を関数に 渡す必要がなくなる。

なお、C言語では言語仕様で決められた番兵もある。 文字列では \0 、ファイルでは EOF である。

プログラムの解法

課題1-1

配列変数 hindo の x 番目の要素 hindo[x] は文字コード x+32 の出現頻度を 格納している。 そのため、書式通りに印刷するには次のように printf を使う。


printf("'%c': %d\n",x+32,hindo[x]);

これを x が 0 から 95 まで繰り返し出力するようにする。 但し、課題の指定にあるように hindo[x] が 0 の場合は出力しない。

このようにして作成した dispHindo 関数を付録に示した。 これを指示されたテストプログラムを用いてテストを行う。 行った結果も付録に示した。 指定された出力例と一致した。

課題1-2

countHindo 関数では、与えられた文字ポインタに対して、文字列が終了する まで一つずつ、文字を読んでいく。読み込んだ文字の変数を c とする。 次に、読んだ文字 c が 32 以上 126 以下であることを確認する。 確認が終わったら hindo[c+32] を 1 加算する。

このようにして作成した countHindo を付録に示した。 そして、課題の指示通りにテストプログラムに組み込み、テストを行う。 このテストでは、"This is a pin." という文字列の文字の頻度を数える。 数えた結果は以下の通りである。

文字頻度
空白3
ピリオド1
T1
a1
h1
i3
n1
p1
s2

実行した結果を付録に示した。 上記の頻度と実行結果が予想と一致するため、正しいプログラムが得られたこ とがわかった。

課題1-3

指定された void fCountHindo(FILE*) は課題 1-2 において文字列から一文 字取り出す代わりに、与えられたファイルハンドルから文字を一文字読むよう に作り替えるだけである。

このようにして作成した fCountHindo を付録に示した。 また、指定された方法でのテストを行った結果も付録に示した。 すべての文字について頻度を計算しテスト結果と一致するかを調べるのは困難 なので、いくつかの文字について検証した。 '*' はファイルハンドルの定義だけで使用しているので、 2 箇所しか使って いない。 また、'F' はファイルハンドルの定義と EOF で使っているので 3 箇所使用し ている。 これを実行結果で見ると確かに一致している。

課題1-4

省略

まとめ

今回は文字頻度を扱う dispHindo, countHindo, fCountHindo, fCountHindoIgnoreCase を作成した。

作成するに当たって、とくに(省略)

参考文献

  1. 講義資料 http://edu.net.c.dendai.ac.jp/ad/2/2009/
  2. B.W.カーニハン, D.M.リッチー著, 石田晴久訳「プログラミング言語C」第二版。共立出版(1989)

付録

課題1-1のプログラム

課題1-1のテスト結果

課題1-2のプログラム

課題1-2のテスト結果

課題1-3のプログラム

課題1-3の実行例

課題1-4のプログラム

課題1-4の実行例

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科