リスト内包記法でズンドコしたい!
最近お仕事で毎日 Python を書いているのですが、リスト内包記法に慣れてきたのでよく使ってます
ある日ふとズンドコキヨシのことを思い出し、リスト内包記法でズンドコを簡潔に書けたら嬉しいなと思いやってみました
もうだいぶ古くなってしまったネタですがよく知らない方はこちらをご参照くださいませ
ズンドコキヨシまとめ
やりたいこと
ズンドコキヨシの仕様を改めて確認すると、以下の動作を満たせばよいということです
-
ズン
orドコ
をランダムに出力する - 直近5回の出力が
'ズン', 'ズン', 'ズン', 'ズン', 'ドコ'
の場合にキ・ヨ・シ!
を返して処理を終了する
これをリスト内包記法でいい感じにズンドコできないかなーと
イメージとしては以下のようなコードでズンドコが定義できたらなーと夢を持ちました
[x for x in zundoko()]
そして上記のコードが評価されるとこのようなリストが生成されて欲しいです
['ズン', 'ドコ', 'ズン', 'ズン', 'ズン', 'ズン', 'ズン', 'ドコ', 'キ・ヨ・シ!']
Level1. ジェネレーターでズンドコ
どうやらジェネレーターを上手く使えばできそうだということがわかったので、これまでジェネレーターについてあまり詳しく知らなかったのですが調べながらやってみました
まずできたのが以下のようなコードです
import random
def zundoko():
history = []
while True:
history.append(random.choice(['ズン', 'ドコ']))
yield history[-1]
if history[-5:] == ['ズン', 'ズン', 'ズン', 'ズン', 'ドコ']:
yield 'キ・ヨ・シ!'
break
print([x for x in zundoko()])
非常に基本的なジェネレーターです
Level2. ジェネレーター内で再帰する
Level1 のものだと while True
が入るところがちょっと引っかかり再帰にしたいなーと思いました
そして考えたのが以下のコードです
import random
def zundoko(history=None):
if history is None:
history = []
if history[-5:] == ['ズン', 'ズン', 'ズン', 'ズン', 'ドコ']:
yield 'キ・ヨ・シ!'
return
history.append(random.choice(['ズン', 'ドコ']))
yield history[-1]
yield from zundoko(history)
print([x for x in zundoko()])
Level3. 生成と判定を別にする
Level2 のものは値の生成とズンドコ判定が同じジェネレーターの中にあるので、そこを切り離したいなーと思いました
そして考えたのが以下のコードです
import random
def zundoko_gen():
yield random.choice(['ズン', 'ドコ'])
yield from zundoko_gen()
def zundoko():
history = []
for x in zundoko_gen():
history.append(x)
yield history[-1]
if history[-5:] == ['ズン', 'ズン', 'ズン', 'ズン', 'ドコ']:
yield 'キ・ヨ・シ!'
break
print([x for x in zundoko()])
Level4. 更に過去の window を生成する処理を別にする
(※更によい新たなパターンをコメントを頂き追記しました!)
Level3 では過去に生成されたズンドコを保持する処理も混ざってしまっているところが気持ち悪かったのですが、指定した個数分の window を生成するジェネレーターを間に挟むことで、より各々のジェネレータの役割が明確になりシンプルな構成にすることができました
import random
def zundoko_stream():
"""
無限に 'ズン' or 'ドコ' を生成する
"""
yield random.choice(['ズン', 'ドコ'])
yield from zundoko_stream()
def zundoko_window(length):
"""
生成されたズンドコの直近 length 個分の window を生成する
"""
history = []
for zundoko_val in zundoko_stream():
history += [zundoko_val]
history = history[-length:]
yield history
def zundoko(pattern=['ズン'] * 4 + ['ドコ']):
for window in zundoko_window(len(pattern)):
yield window[-1]
if window == pattern:
yield 'キ・ヨ・シ!'
break
print([x for x in zundoko()])
ズンドコを無限に生成するストリーム
-> 任意の数だけ切り取る window
-> window に対するパターンマッチ
というより汎用的なズンドコストリーム処理になりましたね!
まとめ
リスト内包記法でズンドコというより、ジェネレーターでズンドコになってしまいました
ジェネレーターは奥が深いですね
なんかもっとスッキリできそうな感じがしますが、現状の僕の理解度だとこの程度です
使いこなすにはまだまだ修行が必要だと感じました
みんなもジェネレーターでリスト内包記法しよう!