- ベストアンサー
使い回しの効く関数の作り方
ある関数を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の使い回しができなくなると思い, このような方法にしていますが, これも被積分関数ごとに構造体を用意しなければならず, あまり良くないんじゃないかな~って思っています. もっといい方法があれば教えていただきたいです. よろしくお願いします.
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
現行の方法で問題ないと思いますが、シンプルな作りにするとすれば、以下の2案が考えられます。 1案:各被積分関数の引数の数を固定にしてしまう。(例えば全て10個にする。実際にその引数を使用するかどうかは、関数によりきまるので、呼び出す関数毎に適切な値を設定して呼び出す。使用しない引数は0を指定する等) 2案:被積分関数ごとに構造体を使用している部分をdoubleの配列形式にする。各被積分関数には、doubleの配列の先頭アドレスをパラメータとして渡す。配列の何番目がどのパラメータになるかは、各被積分関数毎の固有の取り決めとする。
その他の回答 (6)
- junneko
- ベストアンサー率38% (7/18)
>可変長引数関数は頻繁に使われたりするものなのですか? 滅多に使われないでしょうね。渡す型を間違えやすいですし。 まあ、配列を渡すのが妥当なところでしょうか。 あるいは、積分クラスを定義して被積分関数を仮想関数としてオーバーライドするというのもありのような気がします。 仮想関数呼び出しのオーバーヘッドが問題になる場合もありそうですが… こんな感じで↓ class integral { .... double integrate(double a,double b); virtual double integrand(double x) = 0 ; .... }; これなら係数の設定などは継承したクラス側でどうにでもできますね。コンストラクタに渡すとか係数変更用のメンバ関数を定義するとか。 >ちなみに va_list を使うときにはループの中で va_start, va_end しないとこけるんじゃないでしょうか>#3 仰せの通りです^^;
お礼
皆様,いろいろと役に立つ情報をありがとうございました.
- ency
- ベストアンサー率39% (93/238)
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)
私なら…次のいずれかですね。 1. 構造体 2. 配列 (ただし要素数もいっしょに渡してあげる) ま、素直に考えれば構造体だと思いますけど。。。 -------------------------------- 〔以下は、完全に私見です。適当に読み捨ててください〕 ちなみに、もっと言えば、私の場合、積分の関数 integral() のパラメータとして被積分関数の係数情報を渡すのって、ちょっとしっくりこないです。 被積分関数は、係数込みでダイレクトに実装してしまいそう。 # あ、それじゃ、yassan_yassan さんのいう汎用的に合わなく # なるのか。 # う~む、むずかしいですね。。。 でも、いろんな関数を作って、そいつを integral() に渡してあげる方が良いと思うんですけど。。。 被積分関数の形なんて、統一的に扱えるものでもないですし。 # 分数関数、指数関数、対数関数…他にもいろいろありますが、 # すべてを係数情報を構造体で網羅するのは、少々無理がある # と思いますし。。。 …ま、ケースバイケースってことで。 # あ、逃げた。。。
お礼
返信ありがとうございます. 「係数込みでダイレクトに実装」と言うのはfxを fx(double x) { return 3.0*x; } とするということですか? この方法だと,係数を変えることができませんよね? 例えばy=axで,aを1,2,3,と変化させたり. このときはaをグローバル変数にします?
- Tacosan
- ベストアンサー率23% (3656/15482)
自分ならまず間違いなく構造体を使います. ちなみに va_list を使うときにはループの中で va_start, va_end しないとこけるんじゃないでしょうか>#3
- junneko
- ベストアンサー率38% (7/18)
#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)
お礼
ありがとうございます。 私も可変長引数を思いついたのですが、 やはり可変長引数の関数を作るのには少し抵抗があって。 可変長引数関数は頻繁に使われたりするものなのですか?
- junneko
- ベストアンサー率38% (7/18)
可変引数を使うとか。 以下、ぱぱっと書いてみました。 #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 参考になれば幸いです。
お礼
回答ありがとうございます。 1案は私も考えていました。 2案は思いつきませんでした。配列で渡せば使用しない引数がなくなるので、コンパイラの警告がなくなりますね。参考にさせていただきます。