# リーダブルコード 第Ⅱ部 ループとロジックの単純化 ## 7章 制御フローを読みやすくする 条件やループなどの制御フローはできるだけ「自然」にする。 コードの読み手が立ち止まったり、読み返したりしないように書く。 ### 7.1 条件式の引数の並び順 条件式の左辺と右辺は、次のようにそろえておくと分かりやすい。 * 左辺:**調査対象の式。可変なものが多い。** * 右辺:**比較対象の式。あまり変化しない。** ### 7.2 if/elseブロックの並び順 if/elseブロックでは、以下のルールで優先順位を決めると良い。 * 条件は**否定形よりも肯定形**を使う。 * **単純な条件**を先に書く。→ifとelseが同じ画面に表示されて見やすくなる。 * **関心を引く条件や目立つ条件**(例 if not file)を先に書く。 ### 7.3 三項演算子 C言語などでは、 if(条件){ a }else{ b } を、「条件?a:b」と、三項演算子を用いて書くことがある。 簡潔になる場合のみ三項演算子を用いて、それ以外は普通にif/elseを用いる。 ### 7.4 do/whileループを避ける do{式}while(条件)ループは、Perl等のプログラミング言語で使われている。 式の部分は最低1回は実行される。 whileの条件式が後ろに来ているため不自然なことが多く、エラーや混乱の原因になりやすい。通常のwhile文で書き直した方が分かりやすい。 ### 7.5 関数から早く返す 関数からのreturnは複数あっても良い。むしろ望ましいこともある。 出口を一つにしたい発想はクリーンアップコードを確実に実行したいことが考えられる。 JavaやPythonではtry…finallyのように、洗練されたクリーンアップコードが提供されている。 ### 7.6 悪名高きgoto C言語以外では、gotoはほとんど必要ない。 gotoは特定の処理へジャンプするコードであるが、ジャンプ先が複数になるとたちまちコードが複雑化してしまい使い物にならなくなる。 通常はループ処理を用いる。 ### 7.7 ネストを浅くする ネストが何重にもかかったコードは理解しにくい。 ネストは既存のコードから途中で新しいコードが加わってできる。 仮に途中での変更の流れが妥当であったとしても、後から見たときにその文脈は失われてしまう。 **変更するときはコードを一歩下がって全体から捉え、ネストが増えて無いかを確認する。** ネストを削除する方法として以下の項目が挙げられる。 * 次のネストがかかる前に、早めに関数をreturnする。 * ループ内部のネストを、continueを用いて削除する。 ### 7.8 実行中の流れを追える? **コードを書く際には、プログラムの全体の流れも追えるようにする必要がある。** 特に、**コードを舞台裏で実行する**スレッドや割り込みハンドラ、例外等には注意が必要である。**いつどんな時に実行されるのかを理解しておかないと、全体の流れを見失ってしまうこともある。** コード全体に占めるこれらの割合を上げすぎないことが重要である。 ## 8章 巨大な式を分割する コードの塊が大きすぎると周囲に悪影響を及ぼすことになる。 **巨大な式は飲み込みやすい大きさに分割する。** ### 8.1 8.2 説明変数・要約変数 式が大きくなる時は、式を表す変数を定義して以降はその変数を用いると簡潔になる。 式の意味を説明する変数は説明変数、長い式を一つにまとめた変数は要約変数と呼ぶ。 ### 8.3 ド・モルガンの法則 (Aの否定 ∩ Bの否定 ∩ Cの否定) = Aの否定 ∪ Bの否定 ∪ Cの否定 (Aの否定 ∪ Bの否定 ∪ Cの否定) = Aの否定 ∩ Bの否定 ∩ Cの否定 この法則を使えば論理式を等価な式に置き換えられる。 ### 8.4 短絡評価の悪用 ブール演算子(and=&&, or=||)は便利であるが、短絡評価を行うものが多い。 すなわち、aがtrueならbは評価されない。 しかし、悪用すると複雑なロジックになってしまうこともある。**無理に一行に押し込むのは避け、後で見やすいコードを書くよう意識する。** ### 8.5 複雑なロジックと格闘する 複数の場合分けが重なるような場合、式はどうしても複雑になりやすく、バグを見逃しやすい。 そのような時は、より優雅な手法が無いかを考えてみる。 * **反対を見つける**:2つのサイコロの和が3以上になる確率を求めるのに、和が2である確率を取り除けばよいのと同じように、**場合分けの反対を考えてみる**と単純な式になることもある。 ### 8.6 巨大な文を分割する 複雑な処理が一つにまとまっていると、巨大な文となって襲い掛かってくる。 ここでもまずは、**同じ処理を変数で定義しておき、短い変数名でメイン処理を記述するのが有効である。** その結果、**読みやすくなるだけでなくタイプミスが減ったり、変更の対応も容易になる。** ### 8.7 もうひとつの創造的な方法:マクロ化 フィールド名が異なるが、同じ形の処理が連続している場合、**マクロ化**を行う事で簡単に処理できる。 ## 9章 変数と読みやすさ 変数は、適当に使うと読みにくさの原因となる。 ### 9.1 変数を削除する #### 役に立たない一時変数 ただ置いただけの変数は役に立たない。 一時変数として役立つのは以下の3つの時である。 * **複雑な式を分割する時** * **意味をより明確にする時** * **重複コードを削除する時** #### 中間結果を削除する 処理の中間結果を保持するために使うような変数も役立たずである。 中間結果の数値をそのまま最終処理に用いてあげれば良い。 **タスクはできるだけ早く終わらせる。** #### 制御フロー変数を削除する 変数doneを置き、ループを抜け出す時にtrueにしてcontinueするようなコードは冗長である。 break文を使えば、変数は不要である。 ### 9.2 変数のスコープを縮める **グローバル変数はできる限り避ける。** どこでどのように使われるのかの追跡が難しく、ローカル変数と混同しやすく名前空間を汚染する。 **グローバル変数に限らず、全ての変数のスコープを縮める。** #### 変数のスコープの縮め方 * クラス全体に変数定義を置かず、メソッド内で定義する。 * クラス変数staticを宣言する。 * 大きなクラスを小さなクラスに分割する。 #### JavaScriptでプライベート変数を作る ```javascript= const submit_form = (function(){ const submitted = false; return function (form_name){ if(submitted){ return; } //処理内容を記述 submitted = true; }; }());//外側の無名関数がすぐに実行されて内側の関数を返している。→プライベート化 ``` #### JavaScriptのグローバルスコープ const, let, var等をつけずに変数を使った場合、その変数はグローバル変数とみなされる。 グローバル変数を使わないためにも、変数宣言のconst, let, varが必ずついているか確認する。 #### JavaScript, Pythonのネストしないスコープ Javaの場合、ネストの状態に関わらず、ブロックの内側で定義した変数はローカル変数となる。 しかし、JavaScriptやPythonでは、ネストしている関数全体で有効になるので注意が必要である。 #### 変数の位置を下げる 初期のC言語は、全ての変数を最初に定義していたが、最初からすべてを記述する必要はない。**変数の定義は変数を使う直前に記述する。** ### 9.3 変数は一度だけ書き込む 変数は、絶えず変更され続けると値を追跡しにくい。 そこで、**一度変数を定義したら以後新たに定義しない**のがセオリーである。 C++やJavaScriptの**const**、Javaの**final**はその最たる例である。 ###### tags: `読書`