ダイナミックマット。 C ++の関数

こんにちは、Habrausers。

最近、私はここでC ++の匿名関数に関する記事を読みました。そのとき、頭の中で思いつきました。数学で馴染みのある関数を扱うためのクラスを急ぐ必要があります。 つまり、実際の引数を取り、実際の値を返します。 実装を考えずに、そのようなオブジェクトをできるだけ簡単に処理する機会を与える必要があります。

そして、これが私がどのように実装したかです。



問題。


これらのすべてのラムダ式は、少なくとも私にとっては時々奇妙に振る舞います。 これはおそらく、これらの式を作成するメカニズムがどのように機能するかを完全に理解できないという事実によるものです。 既存のプリミティブアクションに基づいて大きな関数を作成します。 f(x)= a(x)+ b(x)のようなもの。 そしてこれは、関数の構築における中間リンクとしてのみ作成されたすべてのラムダは、それらにアクセスするために保存されなければならないことを意味します。 今、私はより明確に説明しようとします。



あるプロシージャが関数AとBのペアを取り、5A + Bなどの新しい式を返すとします。このプロシージャは、ラムダ5Aを作成し、5AとBを使用して5A + Bを作成します。ラムダ5Aがスコープから消え、返された式が機能しなくなります。



解決策。


すべてのラムダの1つのグローバルコレクションを作成することにしました。構築中のすべての式はその中に格納され、コレクションの要素へのポインタのみがオブジェクトに存在します。 コードを説明します。



#include <xstddef> #include <functional> #include <list> typedef std::tr1::function<double(double)> realfunc; // y = f(x) as in maths class func_t { protected: static std::list<realfunc> all_functions; //   realfunc *f; //    public: func_t(); func_t(const double); func_t(const realfunc&); func_t(const func_t&); ~func_t() {}; friend func_t operator+ (const func_t&, const func_t&); friend func_t operator- (const func_t&, const func_t&); friend func_t operator* (const func_t&, const func_t&); friend func_t operator/ (const func_t&, const func_t&); friend func_t operator^ (const func_t&, const func_t&); func_t operator() (const func_t&); double operator() (const double); };
      
      







手始めに、 デザイナー向け

デフォルトのコンストラクター(パラメーターなし)は、引数を返す関数を作成します。 これが出発点になります。 f(x)= x。

残りは明確です。2番目は定数関数-f(x)= cを作成し、3番目は目的の型のラムダをクラスのオブジェクトに変換します。後者は単なるコピーコンストラクターです。



コンストラクターの実装により、クラスのすべてのメソッドがどのように配置されているかがすぐに示されます。



 func_t::func_t() { f = &(*all_functions.begin()); } func_t::func_t(const double c) { func_t::all_functions.push_back( [=](double x)->double {return c;} ); this->f = &all_functions.back(); } func_t::func_t(const realfunc &realf) { func_t::all_functions.push_back(realf); this->f = &all_functions.back(); } func_t::func_t(const func_t &source) { this->f = source.f; }
      
      







ご覧のとおり、ラムダを作成し、コレクションの最後にプッシュして、ポインターを含むオブジェクトを返します。

最初のコンストラクターについてすぐに説明したいと思います。 私が言ったように、「引数」の作成、すなわち function f(x)= xは、私のクラスのほとんどすべての作業の始まりです。したがって、この関数を強調表示し、この式をコレクションの最初のセルに配置することにしました。 そして、デフォルトのコンストラクターを呼び出すと、オブジェクトは常にコレクションの最初の要素へのポインターを取得します。



ああ、ほとんど忘れていました。すべてのデザイナーを暗黙的な変換に使用できるため、主な使いやすさが向上します。



次に、 演算子 。 それらを使用すると、すべてが簡単です。 そのうちの3つを示します。1つ目は加算の実装、2つ目の関数の構成、3つ目の推定です。



 func_t operator+ (const func_t &arg, const func_t &arg2) { realfunc realf = [&](double x)->double { return (*arg.f)(x) + (*arg2.f)(x); }; return func_t(realf); } func_t func_t::operator() (const func_t &arg) { realfunc realf = [&](double x)->double { return (*f)((*arg.f)(x)); }; return func_t(realf); } double func_t::operator() (const double x) { return (*f)(x); }
      
      







簡単ですね。 最初の2つでは、必要なラムダが再度作成され、オブジェクトのコンストラクターに送信されます。 3番目の方法では、一般に景品=)

メソッドの引数にアクセスするために、リンクによる環境転送(ラムダの前の[&])を使用するすべての場所で説明します。



原則として、それだけです。 技術的な詳細がいくつかあります。 私は静的フィールドを初期化するのがあまり得意ではないので、私はかつてどこかでスパイしていた非常に面倒でい方法を思い出さなければなりませんでした。



 protected: static class func_t_static_init_class { public: func_t_static_init_class(); }; static func_t_static_init_class func_t_static_init_obj; ... //Static: std::list<realfunc> func_t::all_functions = std::list<realfunc>(); func_t::func_t_static_init_class func_t::func_t_static_init_obj = func_t::func_t_static_init_class(); func_t::func_t_static_init_class::func_t_static_init_class() { func_t::all_functions.push_back( [](double x)->double {return x;} ); }
      
      







まあ、あなたは理解しています、私はリストを作成し、私がすでに言及した最初の要素を突き出します。

この恐怖をおorび申し上げます。プログラムを学ぶだけです。



ボーナス。


それは基本的にそれです。 私はすでに純粋に楽しみのためにやったことがいくつかあります(実際、すべてが好きです)。



最初に、cmathからいくつかの関数をオーバーロードします。

 friend func_t sin(const func_t&); friend func_t cos(const func_t&); friend func_t tan(const func_t&); friend func_t abs(const func_t&); ... func_t sin(const func_t& arg) { realfunc realf = [&](double x)->double { return sin((*arg.f)(x)); }; return func_t(realf); }
      
      







第二に、デリバティブとアンチデリバティブなしでそこにある=)



 static double delta_x; func_t operator~ (); func_t operator| (const double); ... double func_t::delta_x = 0.01; func_t func_t::operator~ () { realfunc realf = [&](double x)->double { return ((*f)(x + delta_x / 2) - (*f)(x - delta_x / 2)) / delta_x; }; return func_t(realf); } func_t func_t::operator| (double first_lim) { realfunc realf = [=](double x)->double { double l_first_lim = first_lim; //will move with this copy of first_lim double area = 0; bool reverse = x < first_lim; //first_lim > second_lim? if (reverse) { l_first_lim = x; x = first_lim; } double l_delta_x = delta_x; //step while (l_first_lim < x) { //move along the whole span if ((l_first_lim += l_delta_x) > x) //stepped too far? l_delta_x += x - l_first_lim; //the last l_delta_x may be shorter /* integral summ, the point is chosen between the point for f(x) is chosen between l_first_lim and l_first_lim + l_delta_x */ area += l_delta_x * (*f)(l_first_lim + l_delta_x / 2); } return area * (reverse?-1:1); }; return func_t(realf); }
      
      







このコードの説明は省略しますが、退屈であり、ある点での微分の絶対的な単純な実装であり、上限が可変の積分です(どちらの場合も、制限の代わりに固定デルタが使用されます)。



おわりに


まあ、それがすべてです。 おもしろかったです。 そのようなことが意味をなすかどうかはわかりませんが、私はクラスであらゆる種類の&と*を練習しましたが、私にとってこれは重要です=)ご静聴ありがとうございました。



おっと! もう一つ。


はい、それを使用する方法。

たとえば、次のように:

 func_t f1 = cos(5 * func_t() + 8);
      
      





これは、明らかに、関数を作成します

f1(x)= cos(5x + 8)



またはこのように:

 funt_t x = func_t(); func_t f = x + f1(x / ~f1);
      
      





これはf(x)= x + f1(x / f1`(x))です



または、最終的には次のようになります。

 realfunc g = [](double->double) { ... //   ,    ... } func_t f3 = g;
      
      





結論番号2。


さて、確かに。 ご清聴ありがとうございました!

どちらかといえば、完全で未完成のコードはこちらです。



All Articles