一般的にネストは深くなりすぎないほうがいいといわれています。
何段以上が「深い」とするかはコーディング規約に依存しますが、多数の条件を調べようとすると「深くせざるを得ない」と思っていました。
例えとして、複数の条件にマッチするTweetを探したいときを例に示します。
ここではTwitterの検索にはTweepyを使用しています。
条件
・特定のアカウントでない
・NG wordが含まれていない
・RT数が30以上
・お気に入れられが30以上
・日本語
まず思いつくコードは以下のようになると思います。
keyword="電子工作" NG_ScrName =[aaa,bbb,ccc,ddd] for tweet in api.search(q=keyword, count=200): # 検索 txt = tweet.text #テキスト本文 if not tweet.user.screen_name in NG_ScrName : if not has_NGWord_in(txt): if tweet.retweet_count >= 30: if tweet.favorite_count >= 30: if is_japanese(txt): print(txt)
ネストを深くしてはいけない信者の方からすれば、典型的なNG例です。
じゃあ、こうすればどうでしょう?
keyword="電子工作" NG_ScrName =[aaa,bbb,ccc,ddd] for tweet in api.search(q=keyword, count=200): # 検索 txt = tweet.text #テキスト本文 if not tweet.user.screen_name in NG_ScrName and not has_NGWord_in(txt) and tweet.retweet_count >= 30 and tweet.favorite_count >= 30 and is_japanese(txt): print(txt)
ネストは深くはないですが、わかりにくさでは同じです。
また、1行の長さでもコーディング規約に引っかかる可能性があります。
1つの解決例が以下のような形ではないでしょうか?
keyword="電子工作" NG_ScrName =[aaa,bbb,ccc,ddd] for tweet in api.search(q=keyword, count=200): # 検索 txt = tweet.text #テキスト本文 if tweet.user.screen_name in NG_ScrName : continue if has_NGWord_in(txt): continue if tweet.retweet_count < 30: continue if tweet.favorite_count < 30: continue if not is_japanese(txt): continue print(txt) #上記のいずれのifにも当てはまらないとき
このとき、判断条件を裏返して非該当の場合にその検索結果は無視します。
continue
はfor
に対して、次のlistのオブジェクトへ進むという意味です。
例えば、if tweet.retweet_count < 30:
に該当すれば、 if tweet.favorite_count < 30:
とif not is_Japanese(txt):
は評価されません。
アーリー・コンティニュー(early continue)というらしいです。
ちなみに、break
は該当すると、以降のfor
が評価されません。
NG wordが含まれているかどうかを判定する関数は以下のようなものを定義しました。
def has_NGWord_in(txt): for word in NG_Word: if txt.find(word) > -1: return True return False #アーリー・リターンしなかったらFalse
引数にTweetの本文を渡して、リストで定義されたNG_Word
のすべてについて、含まれていないときFalse
を返すbool型の関数です。
対象のキーワード(ここでの引数)がリストに含まれているかどうか?であればif not in [List]
の形で処理できますが、リストの全てのキーワードがという、逆の形式なので、このような関数を作りました。(もしかしたらやり方あるのでしょうか?)
forのループで、すべてのNG_Wordについて評価し、一つでも含まれていればTrue
になります。
1つでも含まれていれば…なので、その時点でreturn
し、関数を終了します。
これをアーリー・リターン(early return)といいます。
また、is_japanese(txt)
のように関数にまとめることによってネストを浅くする方法もあります。
def is_japanese(string): for ch in string: name = unicodedata.name(ch) if "HIRAGANA" in name or "KATAKANA" in name: return True return False
この日本語判定は↓で紹介したものです。
s51517765.hatenadiary.jp
まとめ
アーリー・コンティニュー(early continue)とアーリー・リターン(early return)の使い方を実例をもとにまとめました。アーリー・リターンは言葉としてはよく聞きますね。コードのわかりやすさのために使うもの、という認識はありました。
しかし、どういったときに使えばいいのか?というのがよくわからないでいました。
ifやforのネストが深いときには、こういった手法を使うと、読みやすくなります。
ほかに計算量(ステップ数)の削減にも効果があるかとも思いましたが、なくはないですが一般的にはO(n)の次元で削減しても大した効果はないとされています。
参考
qiita.comteratail.com