# 式の評価
:::{container} prog-cpp
## qbpp::MapListを使用した評価
式の値は,変数とその値のペアのリストとして全要素への値の割り当てを提供することで簡単に計算できます.
リストは **`qbpp::MapList`** オブジェクトとして定義できます.
例えば,以下のプログラムは $(x,y,z)=(0,1,1)$ に対して関数 $f(x,y,z)$ を計算します.
```{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.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`** オブジェクトは全てゼロの割り当てで初期化されます.
`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` のメンバ関数 **`comp_energy()`** はエネルギー値を計算し,内部にキャッシュすることに注意してください.
また,ソルバーが返す解オブジェクトは,既にエネルギー値が計算されキャッシュされています.
再計算せずにエネルギーを取得するには,以下に示すようにメンバ関数 **`energy()`** を使用できます:
```{literalinclude} /../programFiles/cppPrograms/advanced/evaluating-expressions-program4.cpp
:language: cpp
:caption: evaluating-expressions-program4.cpp
```
このプログラムでは,`sol.energy()` は正しく0を返します.
しかし,変数 `z` をフリップした後,キャッシュされたエネルギー値は無効になります.
キャッシュが無効な状態では,`sol.energy()` の呼び出しだけでなく `std::cout << sol` による表示もエネルギーを読むため,以下のように**ランタイムエラー**が発生します(この例ではフリップ直後の出力行で発生):
```{include} /../programFiles/markDown/advanced/evaluating-expressions.md
:start-after:
:end-before:
```
この問題を解決するには,解を変更した後に **`sol.comp_energy()`** を呼び出してエネルギーを明示的に再計算する必要があります:
```{include} /../programFiles/markDown/advanced/evaluating-expressions.md
:start-after:
:end-before:
```
このプログラムは以下の出力を生成します:
```{include} /../programFiles/markDown/advanced/evaluating-expressions.md
:start-after:
:end-before:
```
:::
:::{container} prog-python
## 辞書を使った評価
式の値は,すべての変数への値の割り当てを `{変数: 値}` の辞書として与えることで簡単に計算できます.
辞書は完全な割り当てを構成する
`(変数, 値)` のペアのリストを保持します.
以下のプログラムは,$(x,y,z)=(0,1,1)$ に対して関数 $f(x,y,z)$ を計算します:
```{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(ml)` が $f(0,1,1)$ の値を返します.
このプログラムの出力は以下の通りです:
```{include} /../programFiles/markDown/advanced/evaluating-expressions.md
:start-after:
:end-before:
```
あるいは,辞書リテラルとして直接,または `(変数, 値)` のタプルのリストとして
割り当てを与えることもできます:
```{literalinclude} /../programFiles/pythonPrograms/advanced/evaluating-expressions-program2.py
:language: python
:caption: evaluating-expressions-program2.py
```
辞書形式とタプルのリスト形式は等価で,同じ結果を返します.
## Solを使った評価
解(**`Sol`**)を使って式の値を評価することもできます.
そのためには,まず与えられた式 `f` に関連付けた解 `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()`** はエネルギー値を計算し,
解の内部にキャッシュすることに注意してください.
また,ソルバーが返す解は,既にエネルギー値が計算されキャッシュされています.
再計算せずにエネルギーを取得するには,以下に示すように **`energy`** プロパティを使用できます:
```{literalinclude} /../programFiles/pythonPrograms/advanced/evaluating-expressions-program4.py
:language: python
:caption: evaluating-expressions-program4.py
```
このプログラムでは,`sol.energy` は正しく 0 を返します.
しかし,変数 `z` をフリップした後,キャッシュされたエネルギー値は無効になります.
キャッシュが無効な状態では,`sol.energy` へのアクセスだけでなく `print(sol)` による表示も
エネルギーを読むため,以下のように**ランタイムエラー**が発生します(この例ではフリップ直後の
出力行で発生):
```{include} /../programFiles/markDown/advanced/evaluating-expressions.md
:start-after:
:end-before:
```
この問題を解決するには,解を変更した後に **`sol.comp_energy()`** を呼び出して
エネルギーを明示的に再計算する必要があります:
```{include} /../programFiles/markDown/advanced/evaluating-expressions.md
:start-after:
:end-before:
```
このプログラムは以下の出力を生成します:
```{include} /../programFiles/markDown/advanced/evaluating-expressions.md
:start-after:
:end-before:
```
`comp_energy()` を呼び出した後は,`sol.energy` プロパティも正しい(再計算後の)
エネルギーを返します.
:::