🌀
再帰呼び出しでネストした辞書(dict)を生成する
やりたいこと
-
あいうえお
とA
という文字列から、{'あ': {'い': {'う': {'え': {'お': 'A'}}}}}
という辞書を作る- 整形するとこんな感じ:
{ 'あ': { 'い': { 'う': { 'え': { 'お': 'A' } } } } }
- 整形するとこんな感じ:
- 生成した辞書に対して、同様に
あいういあ
とB
という文字列を渡すと、共通する部分を整理して{'あ': {'い': {'う': {'え': {'お': 'A'}, 'い': {'あ': 'B'}}}}}
のようにする- 整形するとこんな感じ:
{ 'あ': { 'い': { 'う': { 'え': { 'お': 'A' }, 'い': { 'あ': 'B' } } } } }
- 整形するとこんな感じ:
愚直にやるならこんな感じですかね。ちょっと気が遠くなりそうです。
d = {}
d["あ"] = {}
d["あ"]["い"] = {}
d["あ"]["い"]["う"] = {}
d["あ"]["い"]["う"]["え"] = {}
d["あ"]["い"]["う"]["え"]["お"] = "A"
d["あ"]["い"]["う"]["い"] = {}
d["あ"]["い"]["う"]["い"]["あ"] = "B"
print(d)
関数の再帰呼び出しを使ってみる
def dict_mapper1(base_dict, s, mapped):
if type(base_dict) is not dict:
return base_dict
if len(s) == 1:
base_dict[s] = mapped
return base_dict
if len(s) < 1:
return
head = s[0]
rest = s[1:]
try:
base_dict[head] = dict_mapper1(base_dict[head], rest, mapped)
except:
base_dict[head] = dict_mapper1({}, rest, mapped)
return base_dict
if __name__ == '__main__':
d = {}
d = dict_mapper1(d, "あいうえお", "A")
d = dict_mapper1(d, "あいういあ", "B")
print(d) # => {'あ': {'い': {'う': {'え': {'お': 'A'}, 'い': {'あ': 'B'}}}}}
関数の冒頭で下記のようにしているのは、引数を最短マッチさせるためです。
if type(base_dict) is not dict:
return base_dict
つまり、d = dict_mapper1(d, "あいういあ", "B")
と d = dict_mapper1(d, "あい", "C")
との両方があったときに、割り当ての順番に関係なく短い方を優先して下記のように出力されます。
{'あ': {'い': 'C'}}
Python ならでは(?)の方法
Python の引数にミュータブル(変更可能)なオブジェクトを渡すと、関数内から呼び出し元の変数を更新できます。これを利用すると return
なしで書くこともできます。
def dict_mapper2(base_dict, s, mapped):
if type(base_dict) is not dict:
return
if len(s) == 1:
base_dict[s] = mapped
return
if len(s) < 1:
return
head = s[0]
rest = s[1:]
try:
base_dict[head]
except:
base_dict[head] = {}
dict_mapper2(base_dict[head], rest, mapped)
if __name__ == '__main__':
d = {}
dict_mapper2(d, "あいうえお", "A")
dict_mapper2(d, "あいういあ", "B")
print(d) # => {'あ': {'い': {'う': {'え': {'お': 'A'}, 'い': {'あ': 'B'}}}}}
感想:エッシャーの『描く手』 を思い出しました。
Discussion