#include<stdio.h>
#include<stdlib.h>
#define MAXDATA 10000
int main(void)
{
int high,low,mid;
int x,a[MAXDATA];
int n,i,position;
char filename[20];
FILE *fin;
printf("file name = ");/*ファイルの読み込み*/
scanf("%s",filename);
if((fin=fopen(filename,"r"))==NULL){
printf("ファイルをオープンできません\n");
return (1);
}
n=0;
while (fscanf(fin,"%d",&a[n])==1)n++;
fclose(fin);
/*データの整列開始*/
int p,j,k;
int tmp;
for(p = 0;p < n-1; p++){
j=p;
for(k = p+1;k < n; k++){
if(a[j] > a[k]){j = k;}
}
tmp = a[j];
a[j] = a[p];
a[p] = tmp;
}
for(i = 0;i < n; i++){
printf("%d\n",a[i]);
}
/*データの整列終了*/
while (scanf("%d",&x)==1){
/*二分探索*/
position=-1;
high=n-1;
low=0;
while(low <= high && position==-1){
mid = (low + high)/2;
if(a[mid] == x)
position = mid;
else if(a[mid] > x)
high=mid-1;
else if(a[mid] < x)
low=mid+1;
}
printf(" x=%d ---> %d\n",x,position);
}
printf("またお会いしましょう\n");
return(0);
}
関数に置き替える問題です。
関数に置き替える問題です。
Re: 関数に置き替える問題です。
言葉足らずだったので付け足しいたします。まだ、不備があるようでしたらすみません。
C言語で書かれたこのプログラムを、「ファイルからデータを読み込む」「データの整列」「二分探索」をそれぞれ関数として構成して同じ働きをするプログラムに書き換えなさいという課題が出題されたのですが、ファイルからデータを読み込む関数とデータの整列を行う関数はきちんと作れたらしく正常に動作してくれました。
ただ、二分探索のプログラムが、上のプログラムのまま定義したり、少しいじって探したい値が見つかった時はそのデータの入っている添字(mid)を返却し、見つからなかった時は-1を返却する条件をつけて見ましたが、どうしてもうまくいきません。
あまり関数の作成をしたことがなく知識不足なのも問題の一つではあると思いますが、どうしてもうまくいかず、期限だけが過ぎていってしまうため、どなたか教えてくださる方がいらっしゃれば嬉しいです。
よろしくお願いします。
C言語で書かれたこのプログラムを、「ファイルからデータを読み込む」「データの整列」「二分探索」をそれぞれ関数として構成して同じ働きをするプログラムに書き換えなさいという課題が出題されたのですが、ファイルからデータを読み込む関数とデータの整列を行う関数はきちんと作れたらしく正常に動作してくれました。
ただ、二分探索のプログラムが、上のプログラムのまま定義したり、少しいじって探したい値が見つかった時はそのデータの入っている添字(mid)を返却し、見つからなかった時は-1を返却する条件をつけて見ましたが、どうしてもうまくいきません。
あまり関数の作成をしたことがなく知識不足なのも問題の一つではあると思いますが、どうしてもうまくいかず、期限だけが過ぎていってしまうため、どなたか教えてくださる方がいらっしゃれば嬉しいです。
よろしくお願いします。
Re: 関数に置き替える問題です。
「ファイルからデータを読み込む」「データの整列」の関数が正常で、
「二分探索」の関数がうまくいかないとうプログラムを見せてください。
そうすれば、アドバイスできると思います。
期限が気になるのなら、出来るだけ早い返信をお願いします。
「二分探索」の関数がうまくいかないとうプログラムを見せてください。
そうすれば、アドバイスできると思います。
期限が気になるのなら、出来るだけ早い返信をお願いします。
Re: 関数に置き替える問題です。
返信遅くなってしまいました。
下のプログラムが、「ファイルからデータを読み込む関数」と「データの整列」の動きが正常に動き、「二分探索」の動きが正常にいかなかったプログラムです。
よろしくお願いします。
下のプログラムが、「ファイルからデータを読み込む関数」と「データの整列」の動きが正常に動き、「二分探索」の動きが正常にいかなかったプログラムです。
#include<stdio.h>
#include<stdlib.h>
#define DATA 10000
char yomikomu(char filename[20],int a[DATA])
{
int n;
FILE *fin;
if((fin=fopen(filename,"r"))==NULL){
return(-1);
}
n=0;
while(fscanf(fin,"%d",&a[n])==1) n++;
fclose(fin);
return n;
}/*ファイルを読み込む関数*/
int seiretu(int a[DATA],int n)
{
int p,j,k;
int tmp;
for(p=0;p<n-1;p++){
j=p;
for(k=p+1;k<n;k++){
if(a[j]>a[k]) {j=k;}
}
tmp=a[j];
a[j]=a[p];
a[p]=tmp;
}
}/*小さい順に並べる関数*/
int nibuntansaku(int a[DATA],int n,int x)
{
int low,high,mid;
int position;
low=0;
high=n-1;
position=-1;
while(low<=high && position==-1){
mid=(low+high)/2;
if(x=a[mid]){
position=mid;
return(mid);}
else if(a[mid] > x){
low=mid-1;
return(-1);}
else if(a[mid] < x){
high=mid+1;
return(-1);}
}
}
/*二分探索する関数*/
int main (void)
{
char filename[20];
int i,position;
int n,x;
int a[DATA];
FILE *fin;
printf("file name="); scanf("%s",filename);/*ファイルの読み込み*/
yomikomu(filename,a);/*ファイルを開く関数*/
if((fin=fopen(filename,"r"))==NULL){
printf("ファイルをオープンできません。\n");
return(1);
}
n=0;
while(fscanf(fin,"%d",&a[n])==1)n++;
fclose(fin);
seiretu(a,n);/*データを整列する関数*/
for(i = 0;i < n; i++){
printf("%d\n",a[i]);
}
while(scanf("%d",&x)==1){
nibuntansaku(a,n,x);/*二分探索をする関数*/
printf(" x=%d ---> %d\n",x,position);
}
printf("またお会いしましょう\n");
return(0);
}
Re: 関数に置き替える問題です。
・fopen の行から fcloe の行までを関数 yomikomu に移し、
それを 呼び出しているのに、main にはまだ fopen から
fclose までが残っています。
・yomikomu は読み込んだデータの個数を返すのに、
main ではそれを n に代入していません。
・yomikomu は、char よりも int を返したほうがよいでしょう。
・seiretu は、a の中身を入れ替えるだけで、何も返さないので
int ではなく void にしましょう。
・nibuntansaku は無茶苦茶です。元の whileループをそのままコピー
し、そのループの後に return position; を追加しましょう。
・main では、nibuntansaku の返した値を position に代入しましょう。
main の変数と、他の関数内の変数は名前が同じでも実体は異なるものです。
関数の中で n や position に値を代入しても、main の n や position は
変更されません。
関数では、return n; や return position; で値を返し、
main では、n = yomikomi(a, n); や position = nibuntansaku(a, n, x);
のように、関数から返された値を main の変数に代入しましょう。
フォーラムルールに従って、コンパイラなどの環境を書いてください。
gcc だったら、gcc -Wall でコンパイルして、警告などが出ないように
ソースを修正しましょう。
修正したら、またソースを貼ってくださいね。
それを 呼び出しているのに、main にはまだ fopen から
fclose までが残っています。
・yomikomu は読み込んだデータの個数を返すのに、
main ではそれを n に代入していません。
・yomikomu は、char よりも int を返したほうがよいでしょう。
・seiretu は、a の中身を入れ替えるだけで、何も返さないので
int ではなく void にしましょう。
・nibuntansaku は無茶苦茶です。元の whileループをそのままコピー
し、そのループの後に return position; を追加しましょう。
・main では、nibuntansaku の返した値を position に代入しましょう。
main の変数と、他の関数内の変数は名前が同じでも実体は異なるものです。
関数の中で n や position に値を代入しても、main の n や position は
変更されません。
関数では、return n; や return position; で値を返し、
main では、n = yomikomi(a, n); や position = nibuntansaku(a, n, x);
のように、関数から返された値を main の変数に代入しましょう。
フォーラムルールに従って、コンパイラなどの環境を書いてください。
gcc だったら、gcc -Wall でコンパイルして、警告などが出ないように
ソースを修正しましょう。
修正したら、またソースを貼ってくださいね。
Re: 関数に置き替える問題です。
#include<stdio.h>
#include<stdlib.h>
#define DATA 10000
char yomikomu(int a[DATA],char filename[20])
{
int n;
FILE *fin;
if((fin=fopen(filename,"r"))==NULL){
return(1);
}
n=0;
while(fscanf(fin,"%d",&a[n])==1) n++;
fclose(fin);
return n;
}/*ファイルを読み込む関数*/
void seiretu(int a[DATA],int n)
{
int p,j,k;
int tmp;
for(p=0;p<n-1;p++){
j=p;
for(k=p+1;k<n;k++){
if(a[j]>a[k]) {j=k;}
}
tmp=a[j];
a[j]=a[p];
a[p]=tmp;
}
}/*小さい順に並べる関数*/
int nibuntansaku(int a[DATA],int n,int x)
{
int low,high,mid;
int position;
position=-1;
high=n-1;
low=0;
while(low <= high && position==-1){
mid = (low + high)/2;
if(a[mid] == x)
position = mid;
else if(a[mid] > x)
high = mid-1;
else if(a[mid] < x)
low = mid +1;
}
return position;
}
/*二分探索する関数*/
int main (void)
{
char filename[20];
int i,position;
int n,x;
int a[DATA];
FILE *fin;
printf("file name="); scanf("%s",filename);/*ファイルの読み込み*/
n=yomikomu(a,filename);/*ファイルを開く関数*/
seiretu(a,n);/*データを整列する関数*/
for(i = 0;i < n; i++){
printf("%d\n",a[i]);
}
while(scanf("%d",&x)==1){
position=nibuntansaku(a,n,x);/*二分探索をする関数*/
printf(" x=%d ---> %d\n",x,position);
}
printf("またお会いしましょう\n");
return(0);
}
Re: 関数に置き替える問題です。
追加で申し訳ないのですが、「二分探索をする関数」を再帰関数呼び出しをするものに作り変えるときはどのようにすればよいのでしょうか。引数は(a[DATA],int low,int high,int x)でreturnの中でlowやhighの値を変えていくのだと思うのですが、nをどうすればいいのかわかりません。
int nibuntansaku(int a[DATA],int n,int x)
{
int low,high,mid;
int position;
position=-1;
high=n-1;
low=0;
while(low <= high && position==-1){
mid = (low + high)/2;
if(a[mid] == x)
position = mid;
else if(a[mid] > x)
high = mid-1;
else if(a[mid] < x)
low = mid +1;
}
return position;
}
/*二分探索する関数*/
Re: 関数に置き替える問題です。
そのプログラムのソースファイルを a.c として、Act さんが書きました: ↑5年前一応このプログラムで規定通りの動作をしてくれました。ただ、gcc -Wall を用いてコンパイルしたときに、「重複されて定義されています」という分がたくさん出てきてしまったのですが大丈夫なのでしょうか?
Ubuntu(Linux)上でコンパイルすると、
$ gcc -Wall a.c
a.c: In function ‘main’:
a.c:62:9: warning: unused variable ‘fin’ [-Wunused-variable]
FILE *fin;
^
「重複されて定義されています」というエラーメッセージは出ません。
Windows上の Cygwin の gcc だと、
C:\tmp\C>gcc -Wall a.c
a.c: 関数 'main' 内:
a.c:62:9: 警告: 使用されない変数 'fin' です [-Wunused-variable]
FILE *fin;
^~~
「重複されて定義されています」というエラーメッセージが出るとしたら、
そこには、何が重複定義なのか書かれているはずです。
それらを無視しないでください。
さて、そのプログラムで何が定義されているかを見てみましょう。
$ gcc -c a.c
$ nm a.o
U __isoc99_fscanf
U __isoc99_scanf
U __stack_chk_fail
U fclose
U fopen
0000000000000220 T main
000000000000015e T nibuntansaku
U printf
U puts
0000000000000080 T seiretu
0000000000000000 T yomikomu
ここで思い当たることがあったので、次のようにコンパイルしてみました。
$ gcc -Wall a.c a.c
a.c: In function ‘main’:
a.c:62:9: warning: unused variable ‘fin’ [-Wunused-variable]
FILE *fin;
^
a.c: In function ‘main’:
a.c:62:9: warning: unused variable ‘fin’ [-Wunused-variable]
FILE *fin;
^
/tmp/ccCOc8yu.o: 関数 `yomikomu' 内:
a.c:(.text+0x0): `yomikomu' が重複して定義されています
/tmp/cc5hhJex.o:a.c:(.text+0x0): ここで最初に定義されています
/tmp/ccCOc8yu.o: 関数 `seiretu' 内:
a.c:(.text+0x80): `seiretu' が重複して定義されています
/tmp/cc5hhJex.o:a.c:(.text+0x80): ここで最初に定義されています
/tmp/ccCOc8yu.o: 関数 `nibuntansaku' 内:
a.c:(.text+0x15e): `nibuntansaku' が重複して定義されています
/tmp/cc5hhJex.o:a.c:(.text+0x15e): ここで最初に定義されています
/tmp/ccCOc8yu.o: 関数 `main' 内:
a.c:(.text+0x220): `main' が重複して定義されています
/tmp/cc5hhJex.o:a.c:(.text+0x220): ここで最初に定義されています
collect2: error: ld returned 1 exit status
リンクすると、定義がぶつかります。
これではありませんか?
さて、プログラムの方ですが、関数 yoikomu が char を返しています。
fopen に失敗したとき、1を返していますが、これだと、ファイルに
データが 1個しかないとき、1を返すのと区別できません。
-1 を返すべきでしょう。
そして、main には、次のコードを追加したほうがよいでしょう。
if (n <= 0) { puts("データを読み込めません"); return 1; }
main の FILE *fin; は不要です。
関数定義の引数で、配列の要素数は不要です。
int yomikomu(int a[], char filename[])
さらに、これは
int yomikomu(int *a, char *filename)
と解釈されます。
どちらの書き方でも構いません。
Re: 関数に置き替える問題です。
再帰呼出しにするなら、引数を個数の n から、low, high にAct さんが書きました: ↑5年前追加で申し訳ないのですが、「二分探索をする関数」を再帰関数呼び出しをするものに作り変えるときはどのようにすればよいのでしょうか。引数は(a[DATA],int low,int high,int x)でreturnの中でlowやhighの値を変えていくのだと思うのですが、nをどうすればいいのかわかりません。
変更したほうが簡単でしょう。 main での呼び出しは、position = nibuntansaku(a, 0, n - 1, x);
Re: 関数に置き替える問題です。
ありがとうございます。再帰関数呼出しをするときは関数の呼び出しの時にデータの個数を使えば、関数の定義の時にわざわざデータの個数を設定する必要はないということですね。
一つ分からないことがあるのですが、関数yomikomuがcharを返しているというのはどのようなことなのでしょうか。
一つ分からないことがあるのですが、関数yomikomuがcharを返しているというのはどのようなことなのでしょうか。
Re: 関数に置き替える問題です。
nibuntansaku(int a[], int n, int low, int high, int x)Act さんが書きました: ↑5年前ありがとうございます。再帰関数呼出しをするときは関数の呼び出しの時にデータの個数を使えば、関数の定義の時にわざわざデータの個数を設定する必要はないということですね。
のように、個数 n と low/hight の両方を渡す必要はなく、
どちらか一方でいいということです。
関数定義が nibuntansaku(int a[], int low, int high, int x) なら、
関数呼び出しは、position = nibuntansaku(a, 0, n - 1, x); です。
関数定義を nibuntansaku(int a[], int n, int x) にして、
関数呼び出しを position = nibuntansaku(a, n, x); にすることもできます。
int nibuntansaku(int a[], int n, int x)
{
if (n < 1) return -1;
int mid = n / 2;
if (a[mid] == x) return mid;
if (a[mid] > x) return nibuntansaku(a, mid - 1, x);
return nibuntansaku(a + mid + 1, n - 1 - mid, x) + mid + 1;
}
#4 のプログラムで yomikomu が char を返しているのをAct さんが書きました: ↑5年前一つ分からないことがあるのですが、関数yomikomuがcharを返しているというのはどのようなことなのでしょうか。
#5 で「int にしましょう」と指摘したのに、
#6 でも修正してくれなくて、
#7 で、また「関数 yoikomu が char を返しています」と指摘したんですが。
それよりも、
重複されて定義されています」というエラーメッセージが出る問題が
未解決なんですが、どうなっているんですか?
私は、普通のコンパイルではそんなメッセージは出ないといっているんですが。
Re: 関数に置き替える問題です。
yomikomu が char を返しているということの意味がわかりました。失礼しました。
「重複されて定義されています」というエラーメッセージが出る問題がなのですが、普通にコンパイルをしたときは何も出なかったのですがgcc -Wall (ファイル名) (ファイル名).c としたときに出てきたのですが実行には問題がなさそうです。
「重複されて定義されています」というエラーメッセージが出る問題がなのですが、普通にコンパイルをしたときは何も出なかったのですがgcc -Wall (ファイル名) (ファイル名).c としたときに出てきたのですが実行には問題がなさそうです。
Re: 関数に置き替える問題です。
「ファイル名」が具体的に何かはっきりしませんが、例えば と「普通にコンパイル」した後、 を実行すると、重複定義のエラーが出ました。Act さんが書きました: ↑5年前gcc -Wall (ファイル名) (ファイル名).c としたときに出てきたのですが
gccで出力ファイル名を指定するには、-oオプションの後にファイル名を書きます。
x.c
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 関数に置き替える問題です。
すみません。そのコードは間違っていました。かずま さんが書きました: ↑5年前関数定義を nibuntansaku(int a[], int n, int x) にして、
関数呼び出しを position = nibuntansaku(a, n, x); にすることもできます。
次のように訂正します。