.. _details: 実装の詳細 ========== .. _details-expr: Expressions ----------- 関数\ :math:`f(x_0, x_1) = 2x_0 x_1 + \cos(\sin(x_0))`\ は、ライブラリを用いると以下のように書くことができる。 .. code-block:: cpp using tlnc::x; using tlnc::sin; using tlnc::cos; auto f = 2_dc * x<0> * x<1> + cos(sin(x<0>)); この関数を\ :math:`f(1, 2)`\ のように呼び出すにはヘッダ\ ``tlnc/call.hpp``\ をincludeして以下のように書く。 .. code-block:: cpp boost::numeric::ublas::vector args(2); args(0) = 1.0; args(1) = 2.0; tlnc::call(f, args); この関数を用いて数式の記述に関する実装の詳細を述べる。 .. _details-expr-operators: 演算子 ^^^^^^ 演算子のオーバーロードはパターン4以外の動作をする :ref:`2変数関数 `\ と同じように定義されている。 しかし、2変数関数と違って名前空間は\ ``tlnc::expressions``\ に属している。 引数を表す\ ``x<0>``\ や関数\ ``cos``\ などを呼び出した結果のオブジェクトの型も 同じ\ ``tlnc::expressions``\ 名前空間内で定義されたクラスなので、 ``2_dc * x<0>``\ や\ ``x<0> + cos(x<0>)``\ などと書くと `ADL `_ によって\ ``tlnc::expressions``\ 名前空間でオーバーロードされた演算子が呼び出されることになる。 よって、オーバーロードした演算子が\ ``tlnc::expressions``\ 名前空間内で定義されて :cpp:class:`is_expression`\ を満たす\ ``op_mul``\ のようなクラステンプレートを 型に持つオブジェクトを返すようにすれば、 式テンプレートによって数式の構造を保持することができることになり、 演算子を適用した結果を更に式の一部として利用できることになる。 .. _details-expr-constant: 定数関数 ^^^^^^^^ 定数関数は\ ``tlnc::expressions::constant``\ クラステンプレートの テンプレートパラメータに定数を表す型を持たせることで表す。 関数 ^^^^ どのような関数があるかは\ :ref:`expressions`\ を参照。 1変数関数 """"""""" 上の例での\ ``tlnc::cos``\ や\ ``tlnc::sin``\ のような1変数関数に対する呼び出しは、引数の型が 1. :cpp:class:`is_expression`\ を満たすクラス 2. :cpp:class:`is_value`\ を満たすクラス 3. それ以外 のどのパターンに当てはまるかによって結果が変わる。 どのパターンに当てはまるかはSFINAEによって判定している。 各パターンでの動作は以下の通り。 パターン1 ``tlnc::expressions::cos``\ のような\ ``tlnc::expressions``\ 名前空間内の :cpp:class:`is_expression`\ を満たすクラステンプレートに、 テンプレートパラメータとして引数の型を渡したものを型とするオブジェクトを返す。 パターン2 コンパイル時に関数の値を評価し、その結果を数式内で扱える定数に変換する。 パターン3 :ref:`generic`\ の関数を呼び出したときと同じ動作をする .. _details-expr-func-twovars: 2変数関数 """"""""" 冪関数\ ``pow``\ のような2変数関数の場合は、引数の型が 1. 両方とも\ :cpp:class:`is_expression`\ を満たすクラス 2. 片方が\ :cpp:class:`is_expression`\ を満たすクラスで片方が\ :cpp:class:`is_value`\ を満たすクラス 3. 両方とも\ :cpp:class:`is_value`\ を満たすクラス 4. それ以外 のように場合分けし、1変数の場合と同様に結果を返す。 ただし、パターン2の場合は\ :cpp:class:`is_value`\ を満たす方の引数を\ :cpp:class:`constant`\ の テンプレートパラメータに渡したものと同一視し、パターン1と同様の処理をする。 :cpp:class:`is_expression`\ を満たすクラスと、 :cpp:class:`is_expression`\ も\ :cpp:class:`is_value`\ も満たさないクラスを混在させた場合は、 コンパイルエラーになる。 引数 ^^^^ 可変引数の変数テンプレートを特殊化し、 1. 引数が1個の時は\ ``tlnc::expressions::vector_arg`` 2. 引数が2個の時は\ ``tlnc::expressions::matrix_arg`` 3. 引数の個数がそれ以外の時は0個の時に限って\ ``tlnc::expressions::arg`` が型になるようになっている。 プレースホルダー ^^^^^^^^^^^^^^^^ ``tlnc::expressions::placeholder``\ を型に持つ変数を\ ``tlnc``\ 名前空間に ``_1``\ から\ ``_10``\ まで宣言してある。 ``tlnc::holder``\ は変数テンプレート。 Call ---- .. _details-call-eval: 評価 ^^^^ :ref:`Expressionsの詳細 `\ の例のように式を書くと テンプレートが入れ子になったものが\ ``f``\ の型になる。 その構造を木で表すと次のようになる:: add / \ mul cos / | \ | 2 x<0> x<1> sin | x<0> ``f``\ はこの木の根の\ ``add``\ を指していると考える。 葉には定数か引数(\ ``x<>``\ や\ ``x<0>``\ など)かプレースホルダーしか来ないので、 関数を評価するためのタプルが渡されたときに、 * 演算子や関数は子に値を伝播させて子の評価結果に何か操作をしたものを返す * 定数は渡されたタプルの中身に関係なく同じ値を返す * ``x<>``\ は渡されたタプルの0番目をそのまま返す * ``x<0>``\ などのベクトルや行列の要素を表すものはタプルの0番目の特定の要素をそのまま返す * プレースホルダーはタプルの特定の要素をそのまま返す というようにすれば関数の値が評価できることになる。 メモ化 ^^^^^^ メモの実態はタプルである。 引数が与えられたときに式の項や部分式とその評価結果の型を列挙し、 その列挙した項や部分式と評価結果をペア(\ ``std::pair``\ )にしてタプルに入れることでメモを作っている。 タプルを作った後は、タプルの増えた部分の評価をしてメモを更新する。 ある項をタプルに追加するとき、その項の中にある式を先にタプルに追加してからその項自身を追加するようにしている。 そうすることでメモに値を代入する際に子の中の式の評価結果を利用できるようになる。 また、タプルの要素を重複しないようにすることで同じ項を何度も評価しないようにしている。 :ref:`Expressionsの詳細 `\ での例に\ ``double``\ 型のベクトルが渡されたとすると、 次の表の各行をペアにしたようなものを要素として持つタプルが作成され、 左側のオブジェクトと渡されたベクトルを使って右側に具体的な評価結果を代入していく。 ``kv::interval``\ のベクトルが渡されたときは表の右側が全て\ ``kv::interval``\ になる。 +--------------------------------------+--------------+ | 項 | 評価結果の型 | +======================================+==============+ | ``2_dc`` | ``double`` | +--------------------------------------+--------------+ | ``x<0>`` | ``double`` | +--------------------------------------+--------------+ | ``x<1>`` | ``double`` | +--------------------------------------+--------------+ | ``2 * x<0> * x<1>`` | ``double`` | +--------------------------------------+--------------+ | ``sin(x<0>)`` | ``double`` | +--------------------------------------+--------------+ | ``cos(sin(x<0>))`` | ``double`` | +--------------------------------------+--------------+ | ``2 * x<0> * x<1> + cos(sin(x<0>))`` | ``double`` | +--------------------------------------+--------------+