カテゴリー
読みやすいコード

表現を簡素化する別の創造的方法/Another Creative Way to Simplify Expressions

今度は、各式で多くのことが起こっている別の例があります。今回はC++での例です:

void AddStats(const Stats& add_from, Stats* add_to) {
    add_to->set_total_memory(add_from.total_memory() + add_to->total_memory());
    add_to->set_free_memory(add_from.free_memory() + add_to->free_memory());
    add_to->set_swap_memory(add_from.swap_memory() + add_to->swap_memory());
    add_to->set_status_string(add_from.status_string() + add_to->status_string());
    add_to->set_num_processes(add_from.num_processes() + add_to->num_processes());
    ...
}

繰り返しになりますが、あなたの目は長くて似ているがまったく同じではないコードに直面しています。

10秒間の綿密な調査の結果、各行が毎回異なるフィールドに対してだけ同じことを実行していることに気付くかもしれません。

add_to->set_XXX(add_from.XXX() + add_to->XXX()); 

C++では、これを実装するマクロを定義できます:

void AddStats(const Stats& add_from, Stats* add_to) {
    #define ADD_FIELD(field) add_to->set_##field(add_from.field() + add_to->field())
    ADD_FIELD(total_memory);
    ADD_FIELD(free_memory);
    ADD_FIELD(swap_memory);
    ADD_FIELD(status_string);
    ADD_FIELD(num_processes);
    ...
    #undef ADD_FIELD
}

煩雑さを取り除いたので、コードを見て、何が起こっているのかをすぐに理解できます。 各行が同じことをしているのは明らかです。

マクロの使用を頻繁に推奨しているわけではないことに注意してください。実際、マクロはコードを混乱させ、微妙なバグを招く可能性があるため、通常は回避します。

ただし、この場合のように、シンプルで読みやすさに明確なメリットをもたらす場合があります。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

巨大なステートメントを分解する/Breaking Down Giant Statements

この章では、個々の式を分解する方法について説明しますが、より大きなステートメントを分解する場合にも同じ手法が適用されます。 たとえば、次のJavaScriptコードは一度に多くのことを取り入れることができます。

var update_highlight = function (message_num) {
    if ($("#vote_value" + message_num).html() === "Up") {
        $("#thumbs_up" + message_num).addClass("highlighted");
        $("#thumbs_down" + message_num).removeClass("highlighted");
    } else if ($("#vote_value" + message_num).html() === "Down") {
        $("#thumbs_up" + message_num).removeClass("highlighted");
        $("#thumbs_down" + message_num).addClass("highlighted");
    } else {
        $("#thumbs_up" + message_num).removeClass("highighted");
        $("#thumbs_down" + message_num).removeClass("highlighted");
    }
};

このコードの個々の表現はそれほど大きなものではありませんが、すべて一緒に配置されていると、一度にすべてに当てはまる1つの巨大なステートメントを形成します。

幸いなことに、多くの式は同じです。つまり、関数の上部で要約変数としてそれらを抽出できます(これは、DRY(Do n’t Repeat Yourself)原則のインスタンスでもあります)。

var update_highlight = function (message_num) {
    var thumbs_up = $("#thumbs_up" + message_num);
    var thumbs_down = $("#thumbs_down" + message_num);
    var vote_value = $("#vote_value" + message_num).html();
    var hi = "highlighted";

    if (vote_value === "Up") { 
        thumbs_up.addClass(hi); 
        thumbs_down.removeClass(hi);
    } else if (vote_value === "Down") { 
        thumbs_up.removeClass(hi); 
        thumbs_down.addClass(hi);
    } else { 
        thumbs_up.removeClass(hi); 
        thumbs_down.removeClass(hi);
    }
};

var hi = "highlighted"の作成は厳密には必要ありませんが、6つのコピーがあったため、魅力的なメリットがありました。

-入力ミスを防ぐのに役立ちます。 (実際、最初の例では、文字列のスペルが5番目のケースで "highighted"と間違っていたことに気付きましたか?)

-ライン幅をさらに縮小し、コードをスキャンしやすくします。

-クラス名を変更する必要がある場合は、変更する場所は1つだけです。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

例:複雑なロジックとの闘い/Example: Wrestling with Complicated Logic

次のRangeクラスを実装しているとします。

struct Range {
    int begin;
    int end;
    // For example, [0,5] overlaps with [3,8]
    bool OverlapsWith(Range other);
};

次の図は、いくつかの範囲の例を示しています。

endは非排他的であることに注意してください。 だから、A、B、Cは互いに重なり合わないが、Dはすべて重なる。
ここでは、OverlapsWith()を実装する試みがあります。範囲のいずれかの端点が他の範囲内にあるかどうかをチェックします。

bool Range::OverlapsWith(Range other) {
    // Check if 'begin' or 'end' falls inside 'other'.
    return (begin >= other.begin && begin <= other.end) ||
            (end >= other.begin && end <= other.end);
}

Even though the code is only two lines long, there’s a lot going on. The following figure shows all the logic involved.

コードは2行だけですが、多くのことが起こっています。 次の図は、関連するすべてのロジックを示しています。

バグがすり抜けるのは簡単であると考えるには、非常に多くのケースと条件があります。

そういえば、バグがあります。 前のコードは、範囲[0,2]が[2,4]と実際には重なっていないと主張しています。

問題は、<=または<を使用して開始値と終了値を比較するときは注意が必要であることです。 ここにこの問題の修正があります:

return (begin >= other.begin && begin < other.end) ||
 (end > other.begin && end <= other.end);

いいね—このコードは非常に複雑になっています。 誰もがこのコードを読んで、それが正しいことを自信を持って知っているとは期待できません。 どうしようか? この巨大な表現をどのように分解できるでしょうか?

よりエレガントなアプローチを見つける

これは、立ち止まって、全く別のアプローチを考える必要がある場合の1つです。

単純な問題(2つの範囲が重複しているかどうかの確認)として始まったものが、驚くほど複雑なロジックに変わりました。

これは、多くの場合、もっと簡単な方法がなければならないというサインです。

しかし、よりエレガントなソリューションを見つけるには創造性が必要です。

どのようにそれについて行きますか? 1つの手法は、問題を「反対の」方法で解決できるかどうかを確認することです。

状況によっては、配列を逆方向に反復したり、一部のデータ構造を前方ではなく後方に埋めたりすることを意味します。

ここで、OverlapsWith()の反対は「重複しない」です。 2つの可能性しかないため、2つの範囲が重なっていないかどうかを判断することは、はるかに単純な問題であることがわかります。

1.他の範囲は、この範囲が始まる前に終了します。

2.もう一方の範囲は、この範囲の終了後に始まります。

これをコードに簡単に変換できます。

bool Range::OverlapsWith(Range other) {
    if (other.end <= begin) return false;  // They end before we begin
    if (other.begin >= end) return false;  // They begin after we end
    return true;  // Only possibility left: they overlap
}

ここのコードの各行は、はるかに単純です。これは、1回の比較のみを含みます。 これにより、読者は<=が正しいかどうかに集中するのに十分な頭脳力を持つことができます。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

短絡ロジックの悪用/Abusing Short-Circuit Logic

ほとんどのプログラミング言語では、論理演算子は短絡評価を実行します。

たとえば、文 if(a || b)は、aがtrueの場合にbを評価しません。 この動作は非常に便利ですが、複雑なロジックを達成するために悪用されることがあります。
著者の一人が書いたステートメントの例を以下に示します。

assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());

英語では、このコードの意味は次のとおりです。「このキーのバケットを取得します。 バケットがnullでない場合は、占有されていないことを確認してください。」

コードはたったの1行ですが、ほとんどのプログラマーは実際に立ち止まって考えさせられます。 それをこのコードと比較してください:

bucket = FindBucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());

これはまったく同じことですが、2行のコードであっても、理解するのがはるかに簡単です。

そもそも、なぜコードが1つの巨大な式として記述されたのでしょうか。 その時は、それはとても賢いと感じました。

ロジックを簡潔なコードナゲットに分類することには、一定の喜びがあります。

それは理解できます。それはミニチュアパズルを解くようなものであり、私たちは皆、仕事で楽しみたいです。

問題は、コードがコードを読んでいる人にとって、精神的なスピードのバンプだったことです。

キー・アイデア
「巧妙な」コードナゲットに注意してください。他の人が後でコードを読むときに混乱することがよくあります。

これは、短絡動作の利用を避けるべきであることを意味しますか? いいえ。クリーンに使用できるケースはたくさんあります。例えば:

if (object && object->method()) ...

言及する価値のある新しいイディオムもあります。Python、JavaScript、Rubyなどの言語では、「or」演算子は引数の1つを返します(ブール値に変換されません)。そのため、次のようなコードを記述します。

x = a || b || c

a、b、またはcから最初の「真の」値を取り出すために使用できます。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

デ・モルガンの法則を使う/Using De Morgan’s Laws

回路やロジックの講座を受講したことがあるなら、デ・モルガンの法則を覚えているかもしれません。 ブール式を同等の式に書き換えるには、次の2つの方法があります。

1) not (a or b or c) ⇔ (not a) and (not b) and (not c)
2) not (a and b and c) ⇔ (not a) or (not b) or (not c)

これらの法則を覚えていない場合は、

「notを割り当ててand/orをスイッチします。」
(または、逆に言うと、「そうでないものを取り除きます」)。

ブール式を読みやすくするためにこれらの法則を使用することがあります。 例えば、あなたのコードが:

if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");

それは次のように書き直すことができます:

if (!file_exists || is_protected) Error("Sorry, could not read file.");

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

要約変数/Summary Variables

表現が説明を必要としなくても(意味するものを理解することができるため)
新しい変数でその式を捕捉することは依然として有効です。

その目的が単により大きなコードのチャンクを管理し、より簡単に考えられる小さな名前に置き換えることであれば、これを要約変数と呼びます。

たとえば、次のコードの式を考えてみましょう。

if (request.user.id == document.owner_id) { 
    // user can edit this document...
}
...
if (request.user.id != document.owner_id) { 
    // document is read-only...
}

式request.user.id == document.owner_idはそれほど大きくないように見えるかもしれませんが、5つの変数があるため、考えるのに少し時間がかかります。

このコードの主な概念は、「ユーザーは文書を所有していますか?」という概念です。その概念は、要約変数を追加することでより明確に記述できます。

final boolean user_owns_document = (request.user.id == document.owner_id);

if (user_owns_document) {
    // user can edit this document...
}
...

if (!user_owns_document) {
    // document is read-only...
}

あまりよくないように見えるかもしれませんが、(user_owns_document)の場合のステートメントの方が少し簡単です。

また、上部にuser_owns_documentが定義されていると、「これはこの機能全体で参照する概念です」と読者に事前に伝えます。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

説明変数/Explaining Variables

式を分解する最も簡単な方法は、より小さな部分式を取り込む余分な変数を導入することです。

この余分な変数は、「説明変数」と呼ばれることがあります。

なぜなら、サブ表現が何を意味するのかを説明する助けになるからです。
次に例を示します。

if line.split(':')[0].strip() == "root":
…

Here is the same code, now with an explaining variable:

同じコードで、説明変数を用いる:

username = line.split(':')[0].strip()
if username == "root":
…

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
エンジニアリング

プログラミングの命名規則

プログラミングで使用する命名規則のまとめ

キャメルケース(ローワーキャメルケース)

1番先頭の単語の先頭文字は小文字、それ以外の単語の先頭文字は大文字にする。

ラクダのこぶのイメージからつけられた名前です。

例)sendEmailNotification

パスカルケース(アッパーキャメルケース)

単語の先頭文字を大文字にする。

例)SendEmailNotification

スネークケース

単語と単語を 「 _ 」(アンダーバー)でつなぐ。

地面を這うヘビをイメージしてつけられた名前です。

例)send_email_notification

ケバブケース(チェインケース)

単語と単語を 「 - 」(ハイフン)でつなぐ。

串刺しのケバブをイメージしてつけられた名前です。

例)send-email-notification

コンスタントケース

全文字を大文字にする。単語と単語は「 _ 」(アンダーバー)でつなぐ。

例)SEND_EMAIL_NOTIFICATION

カテゴリー
読みやすいコード

あいまいな代名詞を避ける/Avoid Ambiguous Pronouns

代名詞は非常に混乱させることがあります。
読者が代名詞を「解決する」ために余分な作業が必要です。 場合によっては、「それ」または「これ」が何を指しているのかは不明です。

ここに例があります:

// Insert the data into the cache, but check if it's too big first.


このコメントでは、「それ」はデータまたはキャッシュを参照する可能性があります。 おそらく、残りのコードを読むことでそれを理解できるでしょう。

しかし、そうする必要がある場合、コメントのポイントは何ですか?

最も安全なことは、混乱の可能性がある場合に代名詞を "記入する"ことです。 前の例では、「それ」が「データ」だったとしましょう。

// Insert the data into the cache, but check if the data is too big first.

これが最も簡単な変更です。 また、文を再構成して「それ」を完全に明確にすることもできます。

// If the data is small enough, insert it into the cache.

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

コメントをコンパクトに保つ/Keep Comments Compact

C ++型定義のコメントの例を示します。

// The int is the CategoryType.
// The first float in the inner pair is the 'score',
// the second is the 'weight'.
typedef hash_map > ScoreMap;

しかし、なぜそれを説明するのに3行を使用するのですか?1行で説明できますか?

// CategoryType -> (score, weight)
typedef hash_map > ScoreMap;

コメントに3行のスペースが必要なものもありますがが、これはそうではありません。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)