dictの比較クイズ

# dictの比較クイズ
## はじめに
ほとんどのプログラミング言語ではキーと値を対応させたデータ構造を扱うことができます。
JavaではMap、JavaScriptではObject、Pythonではdictなどです。
今回はPythonのdictの比較の挙動が一部予想外だったのでクイズ形式で紹介します。

## 他の言語での挙動
JavaScriptのObjectの比較は非常にシンプルです。
参照が異なる場合は、たとえ中身が一緒であったとしても`false`となります。

```js
const person1 = {name: "taro"};
const person2 = {name: "taro"};

console.log(person1 == person2);
> false
```

逆に参照が等しければ、代入後に値を書き換えても`true`になります。

```js
const person3 = person1;
person3.name = "jiro";

console.log(person1 == person3);
> true
```

それではPythonのdict比較クイズに挑戦してみましょう。

## クイズ
### 第一問
中身が異なるdictを比較すると当然、`False`になります。

```py
person1 = {"name": "taro"}
person2 = {"name": "taro", "age": 20}
person3 = {"name": "jiro"}

print(person1 == person2)
> False

print(person1 == person3)
> False
```

では値がすべて等しいdictを比較するとどうなるでしょうか？

```py
person1 = {"name": "taro", "age": 20}
person2 = {"name": "taro", "age": 20}

print(person1 == person2)
> ???
```

<details>
<summary>第一問の正解はこちら</summary>
<p>

```py
person1 = {"name": "taro", "age": 20}
person2 = {"name": "taro", "age": 20}

print(person1 == person2)
> True
```

予想外だったのではないでしょうか？
Pythonのdictの比較は、参照の比較ではなく、中の値まで比較します。

</p>
</details>

### 第二問
dictの値にさらにdictがある場合はどうなるでしょうか？あるいは値に配列がある場合はどうでしょうか？
ただし値に指定されているdictや配列の中身は等しいです。

```py
person1 = {"name": "taro", child: {"name": "jiro"}}
person2 = {"name": "taro", child: {"name": "jiro"}}

print(person1 == person2)
> ???

person3 = {"name": "taro", children: [{"name": "jiro"}]}
person4 = {"name": "taro", children: [{"name": "jiro"}]}

print(person3 == person4)
> ???
```

<details>
<summary>第二問の正解はこちら</summary>
<p>

```py
person1 = {"name": "taro", "child": {"name": "jiro"}}
person2 = {"name": "taro", "child": {"name": "jiro"}}

print(person1 == person2)
> True

person3 = {"name": "taro", "children": [{"name": "jiro"}]}
person4 = {"name": "taro", "children": [{"name": "jiro"}]}

print(person3 == person4)
> True
```

ともに`True`です。なんとなく傾向がつかめてきました。

</p>
</details>

### 第三問
ネストさせるのは第二問と同様ですが、`person1`、`person2`の`child`に`person1`を入れます。
この場合はどうなるでしょうか？

```py
person1 = {"name": "taro"}
person1["child"] = person1

person2 = {"name": "taro"}
person2["child"] = person1

print(person1 == person2)
> ???
```

ちなみにこのとき、`person1`や`person2`は`child`で無限に参照することができます。

```py
person1["child"]
> {'name': 'taro', 'child': {...}}

person1["child"]["child"]
> {'name': 'taro', 'child': {...}}

person1["child"]["child"]["child"]
> {'name': 'taro', 'child': {...}}
```

これを踏まえて、答えがどうなるか予想してみてください。

<details>
<summary>第三問の正解はこちら</summary>
<p>

```py
person1 = {"name": "taro"}
person1["child"] = person1

person2 = {"name": "taro"}
person2["child"] = person1

print(person1 == person2)
> True
```

おそらく比較の実装がdictの中身の値のを比較する前に、参照が等しいかどうかを判定していると予想することができます。

</p>
</details>

### 第四問
第三問と似ていますが、`person1`の`child`には`person1`、`person2`の`child`には`person2`をそれぞれ詰めます。
今回、この記事を投稿しようと思ったのはこの第四問の挙動が面白かったからなので、ぜひ予測してみてください。

```py
person1 = {"name": "taro"}
person1["child"] = person1

person2 = {"name": "taro"}
person2["child"] = person2

print(person1 == person2)
> ???
```

<details>
<summary>第四問の正解はこちら</summary>
<p>

```py
person1 = {"name": "taro"}
person1["child"] = person1

person2 = {"name": "taro"}
person2["child"] = person2

print(person1 == person2)
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> RuntimeError: maximum recursion depth exceeded in cmp

```

エラーになりました。再帰呼び出しが上限に到達したというエラーです。
参照が異なるとネストを無限にたどってしまうのですね。

</p>
</details>

## さいごに
どのくらい正解したでしょうか？予想外な挙動があって勉強になった、面白かったって方はぜひLGTMしてね♥