dictの比較クイズ
はじめに
ほとんどのプログラミング言語ではキーと値を対応させたデータ構造を扱うことができます。
JavaではMap、JavaScriptではObject、Pythonではdictなどです。
今回はPythonのdictの比較の挙動が一部予想外だったのでクイズ形式で紹介します。
他の言語での挙動
JavaScriptのObjectの比較は非常にシンプルです。
参照が異なる場合は、たとえ中身が一緒であったとしてもfalse
となります。
const person1 = {name: "taro"};
const person2 = {name: "taro"};
console.log(person1 == person2);
> false
逆に参照が等しければ、代入後に値を書き換えてもtrue
になります。
const person3 = person1;
person3.name = "jiro";
console.log(person1 == person3);
> true
それではPythonのdict比較クイズに挑戦してみましょう。
クイズ
第一問
中身が異なるdictを比較すると当然、False
になります。
person1 = {"name": "taro"}
person2 = {"name": "taro", "age": 20}
person3 = {"name": "jiro"}
print(person1 == person2)
> False
print(person1 == person3)
> False
では値がすべて等しいdictを比較するとどうなるでしょうか?
person1 = {"name": "taro", "age": 20}
person2 = {"name": "taro", "age": 20}
print(person1 == person2)
> ???
第一問の正解はこちら
person1 = {"name": "taro", "age": 20}
person2 = {"name": "taro", "age": 20}
print(person1 == person2)
> True
予想外だったのではないでしょうか?
Pythonのdictの比較は、参照の比較ではなく、中の値まで比較します。
第二問
dictの値にさらにdictがある場合はどうなるでしょうか?あるいは値に配列がある場合はどうでしょうか?
ただし値に指定されているdictや配列の中身は等しいです。
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)
> ???
第二問の正解はこちら
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
です。なんとなく傾向がつかめてきました。
第三問
ネストさせるのは第二問と同様ですが、person1
、person2
のchild
にperson1
を入れます。
この場合はどうなるでしょうか?
person1 = {"name": "taro"}
person1["child"] = person1
person2 = {"name": "taro"}
person2["child"] = person1
print(person1 == person2)
> ???
ちなみにこのとき、person1
やperson2
はchild
で無限に参照することができます。
person1["child"]
> {'name': 'taro', 'child': {...}}
person1["child"]["child"]
> {'name': 'taro', 'child': {...}}
person1["child"]["child"]["child"]
> {'name': 'taro', 'child': {...}}
これを踏まえて、答えがどうなるか予想してみてください。
第三問の正解はこちら
person1 = {"name": "taro"}
person1["child"] = person1
person2 = {"name": "taro"}
person2["child"] = person1
print(person1 == person2)
> True
おそらく比較の実装がdictの中身の値のを比較する前に、参照が等しいかどうかを判定していると予想することができます。
第四問
第三問と似ていますが、person1
のchild
にはperson1
、person2
のchild
にはperson2
をそれぞれ詰めます。
今回、この記事を投稿しようと思ったのはこの第四問の挙動が面白かったからなので、ぜひ予測してみてください。
person1 = {"name": "taro"}
person1["child"] = person1
person2 = {"name": "taro"}
person2["child"] = person2
print(person1 == person2)
> ???
第四問の正解はこちら
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
エラーになりました。再帰呼び出しが上限に到達したというエラーです。
参照が異なるとネストを無限にたどってしまうのですね。
さいごに
どのくらい正解したでしょうか?予想外な挙動があって勉強になった、面白かったって方はぜひLGTMしてね♥