関数に置き替える問題です。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Act

関数に置き替える問題です。

#1

投稿記事 by Act » 5年前

コード:

#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);
}
このプログラムを、「ファイルからデータを読み込む」「データの整列」「二分探索」をそれぞれ関数として構成して同じ働きをするプログラムに書き換えたいのですが、二分探索の関数がうまく書けません。どなたか助けてください(><)

Act

Re: 関数に置き替える問題です。

#2

投稿記事 by Act » 5年前

言葉足らずだったので付け足しいたします。まだ、不備があるようでしたらすみません。

C言語で書かれたこのプログラムを、「ファイルからデータを読み込む」「データの整列」「二分探索」をそれぞれ関数として構成して同じ働きをするプログラムに書き換えなさいという課題が出題されたのですが、ファイルからデータを読み込む関数とデータの整列を行う関数はきちんと作れたらしく正常に動作してくれました。
ただ、二分探索のプログラムが、上のプログラムのまま定義したり、少しいじって探したい値が見つかった時はそのデータの入っている添字(mid)を返却し、見つからなかった時は-1を返却する条件をつけて見ましたが、どうしてもうまくいきません。

あまり関数の作成をしたことがなく知識不足なのも問題の一つではあると思いますが、どうしてもうまくいかず、期限だけが過ぎていってしまうため、どなたか教えてくださる方がいらっしゃれば嬉しいです。
よろしくお願いします。

かずま

Re: 関数に置き替える問題です。

#3

投稿記事 by かずま » 5年前

「ファイルからデータを読み込む」「データの整列」の関数が正常で、
「二分探索」の関数がうまくいかないとうプログラムを見せてください。
そうすれば、アドバイスできると思います。
期限が気になるのなら、出来るだけ早い返信をお願いします。

Act

Re: 関数に置き替える問題です。

#4

投稿記事 by Act » 5年前

返信遅くなってしまいました。
下のプログラムが、「ファイルからデータを読み込む関数」と「データの整列」の動きが正常に動き、「二分探索」の動きが正常にいかなかったプログラムです。

コード:

#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: 関数に置き替える問題です。

#5

投稿記事 by かずま » 5年前

・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 でコンパイルして、警告などが出ないように
ソースを修正しましょう。

修正したら、またソースを貼ってくださいね。

Act

Re: 関数に置き替える問題です。

#6

投稿記事 by Act » 5年前

コード:

#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);
}
一応このプログラムで規定通りの動作をしてくれました。ただ、gcc -Wall を用いてコンパイルしたときに、「重複されて定義されています」という分がたくさん出てきてしまったのですが大丈夫なのでしょうか?

Act

Re: 関数に置き替える問題です。

#7

投稿記事 by Act » 5年前

追加で申し訳ないのですが、「二分探索をする関数」を再帰関数呼び出しをするものに作り変えるときはどのようにすればよいのでしょうか。引数は(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: 関数に置き替える問題です。

#8

投稿記事 by かずま » 5年前

Act さんが書きました:
5年前
一応このプログラムで規定通りの動作をしてくれました。ただ、gcc -Wall を用いてコンパイルしたときに、「重複されて定義されています」という分がたくさん出てきてしまったのですが大丈夫なのでしょうか?
そのプログラムのソースファイルを a.c として、
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
定義されているのは、main,nibuntansaku, seiretu, yomikomu の 4つです。

ここで思い当たることがあったので、次のようにコンパイルしてみました。

コード:

$ 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
a.c を 2個コンパイルしてできたオブジェクトファイルを
リンクすると、定義がぶつかります。
これではありませんか?

さて、プログラムの方ですが、関数 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: 関数に置き替える問題です。

#9

投稿記事 by かずま » 5年前

Act さんが書きました:
5年前
追加で申し訳ないのですが、「二分探索をする関数」を再帰関数呼び出しをするものに作り変えるときはどのようにすればよいのでしょうか。引数は(a[DATA],int low,int high,int x)でreturnの中でlowやhighの値を変えていくのだと思うのですが、nをどうすればいいのかわかりません。
再帰呼出しにするなら、引数を個数の n から、low, high に
変更したほうが簡単でしょう。

コード:

int nibuntansaku(int a[], int low, int high, int x)
{
	if (low > high) return -1;
	int mid = (low + high) / 2;
	if (a[mid] == x) return mid;
	if (a[mid] > x) return nibuntansaku(a, low, mid - 1, x);
	return nibuntansaku(a, mid + 1, high, x);
}
main での呼び出しは、position = nibuntansaku(a, 0, n - 1, x);

Act

Re: 関数に置き替える問題です。

#10

投稿記事 by Act » 5年前

ありがとうございます。再帰関数呼出しをするときは関数の呼び出しの時にデータの個数を使えば、関数の定義の時にわざわざデータの個数を設定する必要はないということですね。

一つ分からないことがあるのですが、関数yomikomuがcharを返しているというのはどのようなことなのでしょうか。

かずま

Re: 関数に置き替える問題です。

#11

投稿記事 by かずま » 5年前

Act さんが書きました:
5年前
ありがとうございます。再帰関数呼出しをするときは関数の呼び出しの時にデータの個数を使えば、関数の定義の時にわざわざデータの個数を設定する必要はないということですね。
nibuntansaku(int a[], int n, int low, int high, int x)
のように、個数 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;
}
最後の a[mid] < x の場合が分かりにくいでしょう。
Act さんが書きました:
5年前
一つ分からないことがあるのですが、関数yomikomuがcharを返しているというのはどのようなことなのでしょうか。
#4 のプログラムで yomikomu が char を返しているのを
#5 で「int にしましょう」と指摘したのに、
#6 でも修正してくれなくて、
#7 で、また「関数 yoikomu が char を返しています」と指摘したんですが。

それよりも、
重複されて定義されています」というエラーメッセージが出る問題が
未解決なんですが、どうなっているんですか?
私は、普通のコンパイルではそんなメッセージは出ないといっているんですが。

Act

Re: 関数に置き替える問題です。

#12

投稿記事 by Act » 5年前

yomikomu が char を返しているということの意味がわかりました。失礼しました。

「重複されて定義されています」というエラーメッセージが出る問題がなのですが、普通にコンパイルをしたときは何も出なかったのですがgcc -Wall (ファイル名) (ファイル名).c としたときに出てきたのですが実行には問題がなさそうです。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 関数に置き替える問題です。

#13

投稿記事 by みけCAT » 5年前

Act さんが書きました:
5年前
gcc -Wall (ファイル名) (ファイル名).c としたときに出てきたのですが
「ファイル名」が具体的に何かはっきりしませんが、例えば

コード:

gcc -o x.exe x.c
と「普通にコンパイル」した後、

コード:

gcc -Wall x.exe x.c
を実行すると、重複定義のエラーが出ました。
gccで出力ファイル名を指定するには、-oオプションの後にファイル名を書きます。

x.c

コード:

int main(){}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: 関数に置き替える問題です。

#14

投稿記事 by かずま » 5年前

かずま さんが書きました:
5年前
関数定義を nibuntansaku(int a[], int n, int x) にして、
関数呼び出しを position = nibuntansaku(a, n, x); にすることもできます。
すみません。そのコードは間違っていました。
次のように訂正します。

コード:

int nibuntansaku(int a[], int n, int x)
{
	if (n <= 0) return -1;
	int mid = n / 2;
	if (a[mid] == x) return mid;
	if (a[mid] > x) return nibuntansaku(a, mid, x);
	mid++;
	n = nibuntansaku(a + mid, n - mid, x);
	if (n >= 0) n += mid;
	return n;
}

Act

Re: 関数に置き替える問題です。

#15

投稿記事 by Act » 5年前

「ファイル名」が具体的に何かはっきりしませんが、例えば

コード:

gcc -o x.exe x.c
と「普通にコンパイル」した後、

コード:

gcc -Wall x.exe x.c
を実行すると、重複定義のエラーが出ました。
gccで出力ファイル名を指定するには、-oオプションの後にファイル名を書きます。
[/quote]
-oをつけずにgcc -Wallとしていたのでそれが原因でした。

Act

Re: 関数に置き替える問題です。

#16

投稿記事 by Act » 5年前

かずま さんが書きました:
5年前
かずま さんが書きました:
5年前
関数定義を nibuntansaku(int a[], int n, int x) にして、
関数呼び出しを position = nibuntansaku(a, n, x); にすることもできます。
すみません。そのコードは間違っていました。
次のように訂正します。

コード:

int nibuntansaku(int a[], int n, int x)
{
	if (n <= 0) return -1;
	int mid = n / 2;
	if (a[mid] == x) return mid;
	if (a[mid] > x) return nibuntansaku(a, mid, x);
	mid++;
	n = nibuntansaku(a + mid, n - mid, x);
	if (n >= 0) n += mid;
	return n;
}
ありがとうございます。

返信

“C言語何でも質問掲示板” へ戻る