# 式の評価
:::{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` プロパティも正しい(再計算後の) エネルギーを返します。
:::