# 式の評価 :::{container} prog-cpp ## `qbpp::MapList` を使用した評価 式の値は、**各変数とその値の組(ペア)のリスト**として代入を与えることで簡単に計算できます。 このリストは `qbpp::MapList` オブジェクトとして定義できます。 例えば、次のプログラムは、$(x,y,z)=(0,1,1)$ に対して関数 $f(x,y,z)=x+2y+3z-3$ の値を計算します。 ```{literalinclude} ../../programFiles/cppPrograms/advanced/evaluating-expressions-program1.cpp :language: cpp :caption: evaluating-expressions-program1.cpp ``` このプログラムでは、`qbpp::MapList` オブジェクト `ml` を定義し、`{x, 0}`、`{y, 1}`、`{z, 1}` という代入を `ml` に追加しています。 その後、`f(ml)` を呼び出すことで、$f(0,1,1)$ の値が計算されます。 このプログラムの出力は次の通りです。 ```{include} ../../programFiles/markDown/advanced/evaluating-expressions-program.md :start-after: :end-before: ``` また、次のように**直接代入する**こともできます。 ```{literalinclude} ../../programFiles/cppPrograms/advanced/evaluating-expressions-program2.cpp :language: cpp :caption: evaluating-expressions-program2.cpp ``` ## `qbpp::Sol` を使用した評価 解のオブジェクト(`qbpp::Sol`)は、式のオブジェクト(`qbpp::Expr`)の値を評価するために使用できます。これを行うには、まず与えられた式 `f` に対応する `qbpp::Sol` オブジェクト `sol` を作成します。 新しく作成された `qbpp::Sol` オブジェクトは、**すべての変数に 0 を代入すること**で初期化されます。 `qbpp::Sol` のメンバ関数 `set()` を使用すると、個々の変数に値を割り当てることができます。その後、`f(sol)` と `sol(f)` のどちらも、`sol` に保存されている代入のもとでの式 `f` の値を返します。 さらに、メンバ関数 `comp_energy()` も同じ値を計算して返します。 ```{literalinclude} ../../programFiles/cppPrograms/advanced/evaluating-expressions-program3.cpp :language: cpp :caption: evaluating-expressions-program3.cpp ```
`sol` という`qbpp::Sol`オブジェクトのメンバ関数 `comp_energy()` は、**エネルギー値を計算して内部にキャッシュ**します。 また、ソルバーによって返された`qbpp::Sol`オブジェクトには、すでにエネルギー値が計算されてキャッシュされています。 そのため、**再計算せずにエネルギー値を取得したい場合**は、次のようにメンバ関数 `energy()` を使用します。 ```{literalinclude} ../../programFiles/cppPrograms/advanced/evaluating-expressions-program4.cpp :language: cpp :caption: evaluating-expressions-program4.cpp ``` このプログラムでは、`sol.energy()` は正しく `0` を返します。 しかし、変数 `z` を反転(flip)した後は、**キャッシュされているエネルギー値が無効**になります。 そのため、エネルギーを再計算せずに `sol.energy()` を呼び出すと、次のように**実行時エラー(runtime error)**が発生します。 ```{include} ../../programFiles/markDown/advanced/evaluating-expressions-program.md :start-after: :end-before: ``` この問題を解決するには、解を変更した後に **`sol.comp_energy()` を呼び出してエネルギーを明示的に再計算**する必要があります。 次のように記述します。 ```{include} ../../programFiles/markDown/advanced/evaluating-expressions-program.md :start-after: :end-before: ``` このプログラムは次の出力を生成します。 ```{include} ../../programFiles/markDown/advanced/evaluating-expressions-program.md :start-after: :end-before: ``` ::: :::{container} prog-python ## 辞書を使った評価 式の値は、すべての変数への値の割り当てを `{変数: 値}` の辞書として与えることで簡単に計算できます。 辞書は完全な割り当てを構成する `(変数, 値)` のペアのリストを保持します。 例えば、次のプログラムは、$(x,y,z)=(0,1,1)$ に対して関数 $f(x,y,z)=x+2y+3z-3$ の値を計算します。 ```{literalinclude} ../../programFiles/pythonPrograms/advanced/evaluating-expressions-program1.py :language: python :caption: evaluating-expressions-program1.py ``` このプログラムでは、ペアのリスト `ml = {x: 0, y: 1, z: 1}` で $x=0$, $y=1$, $z=1$ の割り当てを定義しています。そして `f(...)` が $f(0,1,1)$ の値を返します。このプログラムの出力は以下の通りです: ```{include} ../../programFiles/markDown/advanced/evaluating-expressions-program.md :start-after: :end-before: ``` あるいは、辞書リテラルとして直接、または (変数, 値) のタプルのリストとして 割り当てを与えることもできます: ```{literalinclude} ../../programFiles/pythonPrograms/advanced/evaluating-expressions-program2.py :language: python :caption: evaluating-expressions-program2.py ``` ## `Sol` を使用した評価 解のオブジェクト(`Sol`)は、式のオブジェクト(`Expr`)の値を評価するために使用できます。 そのためには、まず与えられた式に関連付けた `Sol` オブジェクトを構築します。 新しく作成された `Sol` オブジェクトはすべてゼロの割り当てで初期化されます。 `sol.set(x, value)` メソッドを使って、個々の変数に値を割り当てることができます。 そして、`f(sol)` と `sol(f)` のどちらも、`sol` に格納された割り当ての下での 式 `f` の値を返します。 さらに、`comp_energy()` メソッドも同じ値を計算して返します。 ```{literalinclude} ../../programFiles/pythonPrograms/advanced/evaluating-expressions-program3.py :language: python :caption: evaluating-expressions-program3.py ``` `sol` のメソッド `comp_energy()` は、**エネルギー値を計算して内部にキャッシュ**します。また、ソルバーによって返された`Sol`オブジェクトには、すでにエネルギー値が計算されてキャッシュされています。 そのため、**再計算せずにエネルギー値を取得したい場合**は、次のようにメンバ関数 `energy()` を使用できます。 ```{literalinclude} ../../programFiles/pythonPrograms/advanced/evaluating-expressions-program4.py :language: python :caption: evaluating-expressions-program4.py ``` このプログラムの出力は以下の通りです: ```{include} ../../programFiles/markDown/advanced/evaluating-expressions-program.md :start-after: :end-before: ``` 解を変更した後(例えば `flip()` を使った後)、キャッシュされたエネルギーは無効になります。 `comp_energy()` を呼び出して明示的に再計算する必要があります: ```{include} ../../programFiles/markDown/advanced/evaluating-expressions-program.md :start-after: :end-before: ``` このプログラムは以下の出力を生成します: ```{include} ../../programFiles/markDown/advanced/evaluating-expressions-program.md :start-after: :end-before: ``` `comp_energy()` を呼び出した後は、`sol.energy` プロパティも正しい(再計算後の) エネルギーを返します。 :::