2013-04-11 35 views
8

長時間のブラウザ、初めての質問者です。私は、さまざまな1D数値統合メソッドを実行するためのスクリプトを作成し、それらをライブラリにコンパイルしました。私は、そのライブラリが可能な限り柔軟に統合できることを考えています。C++:パラメータとして任意の数のパラメータを持つ関数を渡す

ここに私は例を挙げます:統合する関数へのポインタを渡す非常に単純な台形ルールの例です。

// Numerically integrate (*f) from a to b 
// using the trapezoidal rule. 
double trap(double (*f)(double), double a, double b) { 
    int N = 10000; 
    double step = (b-a)/N; 
    double s = 0; 
    for (int i=0; i<=N; i++) { 
    double xi = a + i*step; 
    if (i == 0 || i == N) { s += (*f)(xi); } 
    else { s += 2*(*f)(xi); } 
    } 
    s *= (b-a)/(2*N); 
    return s; 
} 

これは、1つの引数しか取らない単純な関数には最適です。例:

double a = trap(sin,0,1); 

しかし、二次多項式のようなパラメータが多いものを統合したいことがあります。この例では、係数は統合前にユーザによって定義される。コード例:理想的

// arbitrary quadratic polynomial 
double quad(double A, double B, double C, double x) { 
    return (A*pow(x,2) + B*x + C); 
} 

、私はそれを統合するために、このような何かを行うことができるようになります動作しません

double b = trap(quad(1,2,3),0,1); 

しかし、明確に。しかし、その後、私の統合機能は、入力としてオブジェクトを取るために変更する必要がある

class Model { 
    double A,B,C; 
public: 
    Model() { A = 0; B = 0; C = 0; } 
    Model(double x, double y, double z) { A = x; B = y; C = z; } 
    double func(double x) { return (A*pow(x,2)+B*x+C); } 
}; 

の代わりに:私は、メンバ関数としてのメンバーとして係数を持つクラスや興味のある機能を定義することによってこの問題を回避得ています関数ポインタ:

// Numerically integrate model.func from a to b 
// using the trapezoidal rule. 
double trap(Model poly, double a, double b) { 
    int N = 10000; 
    double step = (b-a)/N; 
    double s = 0; 
    for (int i=0; i<=N; i++) { 
    double xi = a + i*step; 
    if (i == 0 || i == N) { s += poly.func(xi); } 
    else { s += 2*poly.func(xi); } 
    } 
    s *= (b-a)/(2*N); 
    return s; 
} 

これはうまくいきますが、結果として得られるライブラリは、クラスModelをどこかで定義する必要があるため、あまり独立していません。また、モデルはユーザーからユーザーに変更できるようにするのが理想的です。そのため、ヘッダーファイルで修正する必要はありません。私は関数テンプレートと関数を使用してこれを動作させようとしましたが、テンプレートはヘッダファイルに定義する必要があります(明示的にインスタンス化しない限り、テンプレートはあまり独立していません)。

まとめ:可変数の入力パラメータを持つ任意の1D関数を受け入れるための統合関数を得ることができますが、スタンドアロンライブラリにコンパイルできるほど十分に独立していますか?事前に感謝の意を表します。

+0

['std :: bind'](http://en.cppreference.com/w/cpp/utility/functional/bind) –

答えて

8

必要なものは、テンプレートとstd::bind()(あるいは、C++ 11を買う余裕がない場合はboost::bind())です。に(例えば、C++ 11のラムダを含む)、我々は関数ポインタから一般化していることを、

template<typename F> 
double trap(F&& f, double a, double b) { 
    int N = 10000; 
    double step = (b-a)/N; 
    double s = 0; 
    for (int i=0; i<=N; i++) { 
    double xi = a + i*step; 
    if (i == 0 || i == N) { s += f(xi); } 
//        ^
    else { s += 2* f(xi); } 
//    ^
    } 
    s *= (b-a)/(2*N); 
    return s; 
} 

お知らせと呼び出し可能オブジェクトのいずれかのタイプを許可する:例えば、これはあなたのtrap()関数がなるものですしたがって、ユーザ提供の関数を呼び出す構文は*f(param)(関数ポインタの場合のみ有効)ではなく、ただf(param)です。

柔軟性については、の2つのハードコードされた機能を考えてみましょう(とそれらをふり、意味のあること):

double foo(double x) 
{ 
    return x * 2; 
} 

double bar(double x, double y, double z, double t) 
{ 
    return x + y * (z - t); 
} 

あなたは今trap()への入力に直接最初の機能の両方を提供することができ、または最後の結合の結果もちろん

#include <functional> 

int main() 
{ 
    trap(foo, 0, 42); 
    trap(std::bind(bar, std::placeholders::_1, 42, 1729, 0), 0, 42); 
} 

、あなたはラムダと一層の柔軟性を得ることができます:いくつかの特定の値(もし引数がバインドする上での自由な選択を持っている)に第二の機能の三つの引数

#include <functional> 
#include <iostream> 

int main() 
{ 
    trap(foo, 0, 42); 
    trap(std::bind(bar, std::placeholders::_1, 42, 1729, 0), 0, 42); 

    int x = 1729; // Or the result of some computation... 
    int y = 42; // Or some particular state information... 
    trap([&] (double d) -> double 
    { 
     x += 42 * d; // Or some meaningful computation... 
     y = 1; // Or some meaningful operation... 
     return x; 
    }, 0, 42); 

    std::cout << y; // Prints 1 
} 

そして、あなたもtrap()、または(あなたがC++ 11を買う余裕ができない場合やboost::functionstd::functionオブジェクトにラップいくつかの呼び出し可能オブジェクトtpは独自のステートフルファンクタを渡すことができます。選択肢はかなり広いです。

ここにはlive exampleがあります。

+0

ありがとう!私は非常にエレガントなので、私はテンプレートの例が好きですが、私はそれが私のために働くだろうか分からない - 私の理解はテンプレート関数を定義し、同じ場所で宣言する必要がありますが、 ) 'を別のライブラリに入れ、それを他のプロジェクトで使用することを宣言します。私は 'std :: function'を' std :: bind'と組み合わせて使用​​し、 'trap()'を 'double trap(std :: function f、double a、double b ) ';唯一の厄介な部分は、 'std :: function'のような単純な関数をsinのように再定義する必要があることです。 – t354

+2

@ t354:はい、テンプレートはその分離問題に悩まされています。その定義はヘッダーファイルでなければなりません。あるいは、定義を '.cpp'ファイルに入れて、あなたのアプリケーションで使用されているすべての可能な引数のテンプレートのいわゆる*明示的インスタンス化*を提供することもできますが、あなたがライブラリであれば不可能ですテンプレートがインスタンス化される方法を予測します。 'std :: function'を署名に入れてもOKです。実行時のオーバーヘッドがあることに注意してください。そうすれば、それがあなたのための関連するオーバーヘッドかどうかを測定する必要があります。 –

+1

@ t354:私は最後の部分を理解していません。「罪のような単純な関数を再定義する」とはどういう意味ですか?これを意味するなら、 'std :: function = sin;トラップ(sin、0、42); '、それは必要ではありません。 'std :: function'オブジェクトを宣言することなく、' trap(sin、0、42); 'を直接呼び出すことができます。 –

3

何をしようとすると、C++ 11我々は、エイリアステンプレートと可変長引数テンプレート上記

template< typename... Ts > 
using custom_function_t = double (*f) (double, Ts...); 

ダブル、変数を取るcustom_function_tを定義を持っていると、この可能

trap(quad, 1, 2, 3, 0, 1); 

を作ることです引数の数。

ので、あなたのtrap機能が

template< typename... Ts > 
double trap(custom_function_t<Ts...> f, Ts... args, double a, double b) { 
    int N = 10000; 
    double step = (b-a)/N; 
    double s = 0; 
    for (int i=0; i<=N; i++) { 
     double xi = a + i*step; 
     if (i == 0 || i == N) { s += f(xi, args...); } 
     else { s += 2*f(xi, args...); } 
    } 
    s *= (b-a)/(2*N); 
    return s; 
} 

使用法次のようになります。アップルLLVM 4.2のコンパイラでテスト

double foo (double X) { 
    return X; 
} 

double quad(double X, double A, double B, double C) { 
    return(A*pow(x,2) + B*x + C); 
} 

int main() { 
    double result_foo = trap(foo, 0, 1); 
    double result_quad = trap(quad, 1, 2, 3, 0, 1); // 1, 2, 3 == A, B, C respectively 
} 

+0

これはちょっと混乱しています –

+0

@MooingDuck私は同意します... – yngccc

関連する問題