読めないコードの二つの理由

本記事は2つの「読めない」について考えてみたいと思います。

「読み方がわからない」と「読んだうえで理解できない」です。

この2つの「読めない」が起きたとき読みやすさを求めるにはどうするかについて考えていきます。

この記事でいいたいこと

  • 目的に近い表現のコードのほうが読みやすい

一行一行は読めるのに何をしているのかわからない場合

早速例になるのですが、

purchase history

EC系プログラムで「購入履歴からユーザごとの最初の購入日を取得する」
というプログラムを書くとします。

クエリではこのような処理は得意ですよね。

select user , min(purchaseDate) from purchaseHistory group by user;

プログラムでも似たようなことをします。

var preHistoryUser = "";
for(int i = 0 ; i < purchaseHistory.length(); i++)
{
    if (preHistoryUser == "" 
        || preHistoryUser  != purchaseHistory[i].user)
    {
        //購入履歴を配列に入れる
    } 
    preHistoryUser = purchaseHistory[i].user;
}

ポイントとしては

  1. ループの外に宣言された変数にループの最後のほうで値を渡す
  2. ループの外に宣言された変数がif文の条件になっている

です。

今回の例では配列を一つずつ見ていったときに
ユーザが切り替わるタイミングが最初の購入日をもっていることに注目します。

一度でも似た構造を見たことがある方や上記表・クエリイメージを見ている人であれば
何をしているのか想像はつきます。

しかし、初めてこの構造を見た人や表イメージ・クエリイメージがないままこの表を読んだ人はどうでしょう。

  • 前の履歴のユーザが空文字か前の履歴のユーザと今の購入履歴のユーザが異なるか?
    • 異なれば配列に入れる
  • 購入履歴のユーザを前の履歴のユーザとする

になるでしょう。

理解できるできないのポイントは
for文とif文の構造から意図が読み取れるかどうかです。

一行一行は読めるけども全体の構造を理解できるかともいえます。

読み方さえわかれば読めるコード

いくつかのモダン言語ではイテレータの機能を充実させていて
for文を使用しない書き方ができます。

前段のfor文は読めた方から読めないといわれたコードを例にします。

こんな感じ。

 var firstPurchaseList = purchaseHistory 
                .GroupBy(x => x.user) 
                .Select(x =>  
                    new 
                    { 
                        user = x.user,
                        purchaseHistory = x.Min(purchaseDate)
                    }); 

C#のリスト型に対しての操作なんですけど馴染がない方に読み方をお伝えします。

  • Group()部分:userでグループ化する
  • Select()部分:グループ化した中で最小のpurchaseDateのデータを選ぶ

つまり、クエリ文と順番は異なりますが同じニュアンスになります。

 var firstPurchaseList = purchaseHistory //from purchaseHistory
                .GroupBy(x => x.user)    //group by user
                .Select(x =>             //select user,min(purchaseDate)
                    new 
                    { 
                        user = x.user,
                        purchaseHistory = x.Min(purchaseDate)
                    }); 

ここでのポイントは読み方を知っているかになります。

メソッドチェーンで改行が入っていますが、

その一行が理解できるかともいえます。

コード内容をそのままとらえると

「ユーザごとにグループ化して最小の購入日をのデータを選択する」

ですかね。

読めない理由は一行と全体の二つの観点がある

二種類のコードを紹介させていただきましたが

それぞれの読みやすさはの観点が異なります。

一つ目は単純なfor文とif文で作られていますがすべての構造を把握しないと理解できない

一行一行は読めるけども全体の構造を理解できるか

二つ目はベースとなる知識がないと理解できない。

その一行が理解できるか」の比較になります。

What(目的)に近いほうが読みやすい

「一行一行は読めるけども全体の構造が理解できるか」と「その一行が理解できるか」を
可読性の観点でどちらかを選べと言われたときどうするか。

どちらも「読めない」を取り扱っていますが、読めたときに
読みにくい/読みやすいで比較したいと思います。

原因が異なる2種類のサンプルのどちらが読みやすいかは

結論、目的により近い方法で書かれているコードのほうが読みやすいです。

可読性というものは手段(How)も重要だと考えています。

日本語でコードを表記した時に違いがでます。

一つ目のfor文を使用している例では
「ユーザが切り替わる時の購入日を選択する」になり

二つ目のfor文を使用していない例では
「ユーザごとにグループ化して最小の購入日をのデータを選択する」のほうが

目的である「購入履歴からユーザごとの最初の購入日を取得する」に近いからです。

読みやすさを選択するうえではHowはWhatに近ければ近いほどわかりやすいです。

まとめ

  • 目的に近い表現のコードのほうが読みやすい

今回は露骨にイテレータを使用した例のほうが読みやすいという例になりましたが

イテレータを用いたコードであってもWhatの意味から遠く

for文がWhatに近いならば読みやすさは逆転するでしょう。

プリミティブな書き方であれ、モダンな書き方であれどちらもエンジニアは知識と技術は日々向上していきます。

以前読めなかったコードが読めるようになりますが、

読みにくい/読みやすいかという戦いはそこから始まります。