• ベストアンサー

使い回しの効く関数の作り方

ある関数をaからbまで積分する関数integralがあります. integral(double (*func)(double, void*), void* t, double a, double b) 例えば次の関数 fx=5x double fx(double x, void* y0) { double y =*(double*)y0; return y*x; } を積分するときには double y=5.0; integral(fx, &y, 1.0, 5.0); としています. さらに gx=5x+5 のようなときは, 構造体を宣言して double gx(double x, void* y0) { parameter p =*(parameter*)y0; return p.y*x + p.z; } としています. これはいい方法なのでしょうか? 関数funcの引数の数を変えてしまうと, integralの使い回しができなくなると思い, このような方法にしていますが, これも被積分関数ごとに構造体を用意しなければならず, あまり良くないんじゃないかな~って思っています. もっといい方法があれば教えていただきたいです. よろしくお願いします.

質問者が選んだベストアンサー

  • ベストアンサー
  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.1

現行の方法で問題ないと思いますが、シンプルな作りにするとすれば、以下の2案が考えられます。 1案:各被積分関数の引数の数を固定にしてしまう。(例えば全て10個にする。実際にその引数を使用するかどうかは、関数によりきまるので、呼び出す関数毎に適切な値を設定して呼び出す。使用しない引数は0を指定する等) 2案:被積分関数ごとに構造体を使用している部分をdoubleの配列形式にする。各被積分関数には、doubleの配列の先頭アドレスをパラメータとして渡す。配列の何番目がどのパラメータになるかは、各被積分関数毎の固有の取り決めとする。

yassan_yassan
質問者

お礼

回答ありがとうございます。 1案は私も考えていました。 2案は思いつきませんでした。配列で渡せば使用しない引数がなくなるので、コンパイラの警告がなくなりますね。参考にさせていただきます。

その他の回答 (6)

  • junneko
  • ベストアンサー率38% (7/18)
回答No.7

>可変長引数関数は頻繁に使われたりするものなのですか? 滅多に使われないでしょうね。渡す型を間違えやすいですし。 まあ、配列を渡すのが妥当なところでしょうか。 あるいは、積分クラスを定義して被積分関数を仮想関数としてオーバーライドするというのもありのような気がします。 仮想関数呼び出しのオーバーヘッドが問題になる場合もありそうですが… こんな感じで↓ class integral { .... double integrate(double a,double b); virtual double integrand(double x) = 0 ; .... }; これなら係数の設定などは継承したクラス側でどうにでもできますね。コンストラクタに渡すとか係数変更用のメンバ関数を定義するとか。 >ちなみに va_list を使うときにはループの中で va_start, va_end しないとこけるんじゃないでしょうか>#3 仰せの通りです^^;

yassan_yassan
質問者

お礼

皆様,いろいろと役に立つ情報をありがとうございました.

  • ency
  • ベストアンサー率39% (93/238)
回答No.6

No5 ency です。 > 「係数込みでダイレクトに実装」と言うのはfxを > fx(double x) > { > return 3.0*x; > } > とするということですか? そういうことです。 > この方法だと,係数を変えることができませんよね? > 例えばy=axで,aを1,2,3,と変化させたり. > このときはaをグローバル変数にします? そうですね。 yassan_yassan さんは、そういうことをやりたいんだと思ったので、最後に「ケースバイケース」と逃げたわけでして。。。 結局、どのような実装にするか次第だと思うんです。 yassan_yassan さんの方法は、ライブラリにベースの関数が用意してあって (一次関数、二次関数など)、その係数をユーザが設定する、というイメージなんだと思います。 私の方法は、integral() とそこに設定する被積分関数の関数型だけ公開しておいて、関数の実装部分はユーザに任せてしまおう、という感じです。 つまり、係数の異なる場合にも、その分だけ別関数を用意してもらうわけです。 いずれにしろ、integral() に被積分関数を設定しなければいけないのであれば、最初からユーザ任せでも良いのでは、ってことです。 えらそうなことをいろいろ言ってきましたが、できるだけ単純な方が好き!という私の性格のなせるところだと、ご理解ください。 # 単純にしておけば、それだけバグを仕込む確率も低くなるわけですし…。 # ← 単なる言い訳? こんな感じでいかがでしょうか。

  • ency
  • ベストアンサー率39% (93/238)
回答No.5

私なら…次のいずれかですね。 1. 構造体 2. 配列 (ただし要素数もいっしょに渡してあげる) ま、素直に考えれば構造体だと思いますけど。。。 -------------------------------- 〔以下は、完全に私見です。適当に読み捨ててください〕 ちなみに、もっと言えば、私の場合、積分の関数 integral() のパラメータとして被積分関数の係数情報を渡すのって、ちょっとしっくりこないです。 被積分関数は、係数込みでダイレクトに実装してしまいそう。 # あ、それじゃ、yassan_yassan さんのいう汎用的に合わなく # なるのか。 # う~む、むずかしいですね。。。 でも、いろんな関数を作って、そいつを integral() に渡してあげる方が良いと思うんですけど。。。 被積分関数の形なんて、統一的に扱えるものでもないですし。 # 分数関数、指数関数、対数関数…他にもいろいろありますが、 # すべてを係数情報を構造体で網羅するのは、少々無理がある # と思いますし。。。 …ま、ケースバイケースってことで。 # あ、逃げた。。。

yassan_yassan
質問者

お礼

返信ありがとうございます. 「係数込みでダイレクトに実装」と言うのはfxを fx(double x) { return 3.0*x; } とするということですか? この方法だと,係数を変えることができませんよね? 例えばy=axで,aを1,2,3,と変化させたり. このときはaをグローバル変数にします?

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

自分ならまず間違いなく構造体を使います. ちなみに va_list を使うときにはループの中で va_start, va_end しないとこけるんじゃないでしょうか>#3

  • junneko
  • ベストアンサー率38% (7/18)
回答No.3

#2です あまりに適当に書きすぎたようです… こっちのほうがご質問からするとわかりやすいかもしれません。 #include <stdio.h> #include <stdarg.h> double integral(double (*func)(double x,va_list va), double a, double b, ...) { double x = 2; double result; va_list va; va_start(va,b); // 本来はこの部分でループして数値積分する result = (*func)(x,va); printf("result = %f\n",result); // ここまで va_end(va); return 0; // 数値積分の結果 } double fnc1(double x,va_list va) { double p1 = va_arg(va,double); double p2 = va_arg(va,double); return x*p1 + x*x*p2; } double fnc2(double x,va_list va) { double p1 = va_arg(va,double); double p2 = va_arg(va,double); double p3 = va_arg(va,double); return x*p1 + x*x*p2 + x*x*x*p3; } int main() { double a = 0; double b = 1; double p1 = 10; double p2 = 20; double p3 = 30; integral(fnc1,a,b,p1,p2); integral(fnc2,a,b,p1,p2,p3); return 0; } 実行結果 result = 100.000000 (←2*10+2^2*20) result = 340.000000 (←2*10+2^2*20+2^3*240)

yassan_yassan
質問者

お礼

ありがとうございます。 私も可変長引数を思いついたのですが、 やはり可変長引数の関数を作るのには少し抵抗があって。 可変長引数関数は頻繁に使われたりするものなのですか?

  • junneko
  • ベストアンサー率38% (7/18)
回答No.2

可変引数を使うとか。 以下、ぱぱっと書いてみました。 #include <stdio.h> #include <stdarg.h> double integral(double (*func)(va_list va), double a, double b, ...) { va_list va; va_start(va,b); (*func)(va); va_end(va); } double fnc1(va_list va) { double x1 = va_arg(va,double); double x2 = va_arg(va,double); printf("x1=%f,x2=%f\n",x1,x2); return 0; } double fnc2(va_list va) { double x1 = va_arg(va,double); double x2 = va_arg(va,double); double x3 = va_arg(va,double); printf("x1=%f,x2=%f,x3=%f\n",x1,x2,x3); return 0; } int main() { double a = 0; double b = 1; double x1 = 10; double x2 = 20; double x3 = 30; integral(fnc1,a,b,x1,x2); integral(fnc2,a,b,x1,x2,x3); } 実行結果: x1=10.000000,x2=20.000000 x1=10.000000,x2=20.000000,x3=30.000000 参考になれば幸いです。

関連するQ&A

  • 関数の掛け算を返す関数

    いま関数の積分を行おうとしています. この積分を行う関数が double integral(double (*func)(double), double a, double b ) {    double ans;    .....    return ans; } となっていて,aからbまでfuncが指す関数を積分して結果を返します. 積分をさせる関数は double f1(double x) { return 3.0*x; } double f2(double x) { return x*x; } となっていて,同じようにg1, g2も用意します.(本当は関数が3つ4つあります.) 例えばf1を積分したいとき, int main() {    double ans = integral(f1, a, b);    printf("%f\n", ans); } ですよね. 自分で積分する関数を選ぶときは,ここに配列で場合分けをして double (*func_f[])(double)={f1, f2}; scanf("%d", &flag); /* flagに0~1を代入 */ ans = integral(func_f[flag], a, b); でいいと思います. さて,そこで本題なのですが, double (*func_f[])(double)={f1, f2}; double (*func_g[])(double)={g1, g2}; としておいて, scanf("%d", flag1); scanf("%d", flag2); func_f[flag1], func_g[flag2]; として関数を入力側から決定して, (例えばflag1=1, flag2=1,であったとして *func_f[1]=x*x *func_g[1]=5.0*x ならば) h(x)=(x*x)*(5.0*x)=5.0*x*x*x という関数を作って, h(x)を指すポインタを double (*h_ptr)(double) とすれば ans = integral(h_ptr, a, b); としたいのです. (f1*g2)を一つの関数としてh(x)=(f1*g2)(x)というように扱うことができればいいと思うのですが. integralの引数で,関数のポインタを2個にすると,汎用性が失われてしまうと思うので,できればそこは変えたくないです. どのようにすればよいのでしょうか? また,「考え方を変えればよい」などの意見も頂きたいです. 皆様,どうぞよろしくお願いします.

  • 引数の個数を変えないで変数(定数)を扱う

    別に積分に限らないと思うのですが、積分を例にして質問させていただきます。 次のようなルーチンがあります。 integral( double (*func)(double), double a, double b ); /* a~bまで関数funcを積分する。 */ そこで、簡単に被積分関数を fx=3*x とすれば、 double fx(double x) { return 3*x; } というようにすれば良いですよね。 でも、例えば fx = exp(x-X) とか fx = x*X のような関数を積分したいときはどうすればよいのでしょうか? Xは変数ですが、xにはよらないので積分の中では定数とみなせます。 ループで X=0 のときにfxを積分 X=1 のときにfxを積分 X=2 のときにfxを積分… というようにしたいのですが、 fxの引数をfx(double x, double X) とすると、プロトタイプ宣言もルーチンの中も書き換えなければならなくなりますよね。 さらに fx = x-X + x' などとなったりすると、さらに書き換えなければならなくなり、せっかくの積分のルーチンをうまく使えません。 Xをグローバルで宣言する方法と、 プログラミングの前に、x-X を x' などと置きかえた式を実際に手計算で作る方法を思いついたのですが、 グローバル変数を使うのはあまりよくないし、手計算では簡単な場合しか置換を思いつかなかったりします。 fxの中で X を宣言して、 double fx(double x) { static double X; double y; y = x-X; X++; return y; } という方法も考えたのですが、どうもイマイチ良くないような… こういう場合に、良い方法はありますか? ここには簡単な関数を書きましたが、少し複雑な関数を積分するので。 質問の意図がうまく伝わらなかったらすいません。 書きにくかったです。

  • main関数の中でパラメータを宣言

    c言語で積分をしています。 a~bまで積分する関数を dobule integral(double (*f)(double), double a, double b) { ・・・・・・ return s; /*sが計算結果*/ } としています。 fx=x*x を積分するときは double fx(double x) { return x*x; } を用意して、 main() { double ans = integral(fx, 0.0, 1.0); } という具合で現在やっています。 本題なのですが、 main関数の中でxを宣言して、そのxで同じように積分をしたいのです。 どうすればよいのでしょうか? 良い方法があれば教えて頂きたいです。

  • C++:ある関数を定義するときに別の関数を用いて定義はできないのですか。

     Visual C++を使っています。  以下のように関数func2において関数func1を使いたいのですがコンパイルするとポインタがどうのこうのと言ってきます。  書き方が間違っているのでしょうか、別にいい方法がありましたら教えてください。参考書はありますので、どの項目を見ればよいと言うアドバイスでも結構です。 const double a=100.0; //グローバル変数aを定義 //関数func1を定義 double func1(double x){ return exp(x); } //関数func2を定義 double func2(double y){ return func1(a)*exp(y);//func(a)は係数。 } 実際はfunc1とfunc2はもっと複雑な形なのでどうしても別々に定義したいです。

  • 構造体の配列を関数に渡すには

    構造体の配列を関数の引数として渡そうとすると エラーになってしまいます ネットで調べてもいまいちわからなかったので ここで質問させてもらいます #include<stdio.h> struct A{   ・    ・    ・ }; void func(struct A *p); int main(void) { struct A x[3][4] = {     ・     ・     ・ }; func(x); return 0; } void func(struct A *p){     ・     ・    ・ } どうすれば渡すことができるのでしょうか? どなたか助言お願いします。

  • 関数へのポインタで

    関数を場合によって使い分けたくて, 次のようにしています. double f1(double x) { return 3*x; } double f2(double x) { return 5*x; } double f3(double x) { return 10*x } /* 同じようにg1, g2, g3があります. */ 次のようなルーチンを使って, void sub(double (*F)(double), ...) { F(a); F(b); } int main() { double (*func_f[])(double)={f1, f2, f3}; double (*func_g[])(double)={g1, g2, g3}; sub(func_f[i], ...) } といった感じにしています. いま,f1*g2のような関数に対してsubを使いたいのですが,この場合はどのようにすればいいのでしょうか? (f1*g2)を一つの関数として扱えることができればいいと思うのですが,方法がわかりません. 教えて下さい. よろしくお願いします.

  • 関数を引数とする方法?

    いつもお世話になっています。 MFCでプログラムをしています。 今、任意の関数(Func1)を 積分する関数(Func2)を作っています。 現在は、被積分関数の数だけ、 積分関数(Func2)を書いているのですが、 非効率的なので、なるべく汎用性を持たせたいと 考えています。 参考書(新C言語入門シニア編)の該当個所で、 クラスでない通常の関数を引数とする場合は、 うまくいったのですが、 クラスのメンバ関数を引数とした場合、 どうしてもコンパイルエラーが 発生してしまいます。 関数Func、I及びエラーメッセージは大凡次のとおりです。今のところ、引数とする関数(Func1)の引数は、 同一個数としています。 <被積分関数の例> double ClassA::Func1 (double a){ return a * 10; } <積分関数> double ClassA::Func2 (double (*f)(double), double a, double b){ return b * f(a); } void classA::Integration() { ... Func2(Func1,a,b); ... } <エラーメッセージ> classA::Integrationの呼び出し箇所で、 「1番目の引数を double(double)からdouble(__cdecl)(double)に 変換できません」 と出ます。 double(double)の部分は合っているようなのですが、 (__cdecl)の部分が違うということまでは 分かりました。 メンバ関数であることが原因のようなので、 Func2での引数宣言を double ClassA::Func2 (double (ClassA::*f)(double), double a, double b){ return b * f(a); } に変えてみたところ、 引数受け渡しのところはクリアするのですが、 Func2(Func1,a,b); の呼び出し時に、Func2が関数ではないという エラーがでます。 アドバイス又は参考URL等を 教えていただければ助かります。 よろしくお願いします。

  • 二つ以上の値を返す関数

    たとえば、a,bという値を関数に入れて、関数の中で変わったa,bを受け取るような方法はないでしょうか。 http://oshiete1.goo.ne.jp/kotaeru.php3?q=138798 の int main(void) { int a=1, b=2; func(&a, &b); printf("%d, %d\n", a, b); return 0; } void func(int *x, int *y) { *x++; *y++; } を実行してみましたが、 「関数 'func' は定義されていません。int 型の値を返す外部関数と見なします。」 と出てきました。 助言をよろしくお願いします。

  • C言語の基本的な質問ですが、関数へのポインタの宣言

    関数へのポインタの質問です。 下のように、関数へのポインタを使ったプログラムを書きました。 (関数へのポインタを理解するためのものなので、実用的な意味はありません。(*^_^*) また、このプログラムはコンパイルもリンクも実行も問題なく出来ます。) #include <stdio.h> int add_func(int,int); (*func_p0) (int,int); int main(void) { int (*func_p1) (int,int); int (*func_p2) ( ); int hoge0,hoge1,hoge2; func_p0=add_func; hoge0=func_p0(3,5); printf("0 : 3+5は%d\n",hoge0); func_p1=add_func; hoge1=func_p1(3,5); printf("1 : 3+5は%d\n",hoge1); func_p2=add_func; hoge2=func_p2(3,5); printf("2 : 3+5は%d\n",hoge2); return(0); } int add_func(int x, int y) { return(x+y); } func_p0のように戻り値の型を書かない場合と、func_p1やfunc_p2のように戻り値の型を書くのとでは何が違うのでしょうか。 func_p0は外部変数ですが、自動変数にする(main関数の中で同様に宣言。)とコンパイルエラーになります。 それはなぜですか。 func_p1のように引数の型が書いてあるのと、func_p2のように引数の型が書いていないのでは何が違うのでしょうか。 int (*func_p2) ( );というのは、int (*func_p2) (void);とは違うんですよね?

  • 二分法で関数の最小値を求める

    題名通りのプログラムを組んだのですが、どうしてもうまくいきません。どこが間違っているのでしょうか?教えてください。 以下プログラム #include< stdio.h > #include< stdlib.h > double minfx( double (*func)(double x), double a, double b ) { if( b-a < 0.0001 ) { if(func(a)>func(b)) { return func(b); } else { return func(a); } } else { if(func(a)>func(b)) { a = (a+b)/2; } else { b = (a+b)/2; } minfx(func,a,b); } } double f1(double x) { return (x-1.0)*(x-1.0); } double f2(double x) { return 3.0*x*x*x*x-4.0*x*x*x-12.0*x*x+10.0; } void main() { double ans1,ans2; ans1 = minfx( f1, 0.0, 5.0 ); ans2 = minfx( f2, 0.0, 5.0 ); printf("関数f1の最小値:%lf \n", ans1); printf("関数f2の最小値:%lf \n", ans2); system("PAUSE"); } これを実行するとf1とf2の値が0.000000といった感じで値が代入できていないと返されます。流れ的には合っていると思うのですが…なお、プログラムの形はできるだけそのままで、関数minfx内のみ変更する形でお願いします。自分としてはそこ以外に原因が考え付かないので…。

専門家に質問してみよう