業務で小一時間悩んでやっと得た答えの備忘録
【事象】
Pythonのfor文でリストの長さの分だけ繰り返し、要素を取り出していく作業。
その際、変数xがある一定の数(例えば5)に達したら、0に戻したい。
でも変数xを0に戻すという定義(x=0)にすると毎回0にリセットされてしまう。
だけど、変数xはfor文でリストの長さの分だけ加算されていき、リストの要素を参照するときにリスト[x]と設定しているものだから、0になったらまた振り出しにもどり、続きから動いてくれなくなる。
さてどうしよう…と小学生でもわかりそうな問題に非理数系出身のアラサーは頭を悩ませた。
【サルと思わしき人間が書いたサンプルコード】
list = ["apple", "orange", "banana", "pineapple"]
for fruit in range(len(list)):
if fruit > 2:
fruit = 0
print(list[fruit])
fruits += 1
else:
print(list[fruit])
「どおじでまたappleに戻っちまうんだよおおおおおおお」
(ほんとはpineappleを出力したい)
もはや藤原竜也のようにIDEの前でのたうち回り、あーでもないこーでもないと試す。
これをサルではなく本物の人間が書いているのだから始末が悪い。
サンプルコードなので多少強引でリアル感がないが、入れ子構造にif文やらfor文を重ねていくとどんどんわからなくなっていくのはもはやお約束だ。
これは初学エンジニアや非エンジニアだけどプログラミングを始めたおにーさんおねーさんなら思い当たることだろう。入れ子構造は頭の良さそうなコードに見えて、その実複雑なだけになっていく死の構文である。
本日最大の学びは、入れ子は重ねずにシンプルにone if, one solutionじゃないけど、if文は重ねずに素直に次のアルゴリズムに渡しちまった方が早い時がある、ということだ。
書いてる時はなんとかその構造の中で解決することがプログラマーとしての能力の見せ所や!と思いがちだが、真実はシンプルでわかりやすく、誰が見ても「ああ、ここが分岐点なのね」とわかるコードを書くことだ。
もし同じような問題にぶち当たっている方がいたら、別に必ずしもコードは一つの構文にまとめなくてもいいんだと頭を切り替えていただきたい。私と同じようなチンパンジーコードを書く必要はない。
【ゼロよ、俺を導いてくれ…という解決コード】
i = 0
for fruit in range(len(list)):
if fruit > 2:
print(list[i])
i += 1
else:
print(list[fruit])
おわかりいただけたであろうか…。
いや、偉そうに言うつもりも俺SUGEEEEでもなく、シンプルに最初にまったく関係ない変数iに0を代入しておくことにより、わざわざn=1,2,3...くらいまで進んでいるfruitをリセットしてやらなくてもいいという話だ。
さらにif分岐が重なる場合は、ちょっとコードが雑になるけど、i以外にもjだのkだのを用意してやればいい。
そうすれば全く別の変数を0から使って、条件を満たしたら加算していって…というファインプレーができるようになる。
いかんせん、サンプルコードが単純なだけに想像がつきづらいかもしれないが、アルゴリズムというのは必ずしも一つのブロックを巨大化してその中で解決しなければならないわけではなく、ダメなら別のアイテムと次のブロックを用意してやればいいんだというシンプルな話である。
言うなれば、マトリョーシカの中身がどんどん小さくて扱いづらくなったら、始めから別に作ったマトリョーシカをまた用意して、その中でやり直せばいいみたいな感じである(そうか?)
ここまで書いておいて何だが、変数はわかりやすい名称をつけるといいよと思う。
なぜならIDEによっては予測変換で最初の数文字さえ打てばサジェストしてくれるので、下手に短すぎてもともと用意された定数なのか判断がつかなくなる心配もないからだ。
ちなみにチンパンジーですら吊るされたバナナをとるのに複数の道具を組み合わせるのだから、人間が一つの構文にウキーとこだわる必要はまるでないという人生訓である(大嘘)