よいデータ構造でコードを簡潔にする


よいデータ構造を使うと、コードが簡潔になる。

例:swicth文から配列の参照へ

swicth文を使って文字列を返す関数
swicth文の変数がenum型(値が0以上)である場合、
配列[enum型の変数]
として値を返すことで、記述が簡単になる。

enumString.cpp
#include <iostream>
#include <string>
#include <vector>
enum Idx { a = 0, b, c };
// vector型を使う例
std::string getMessage(Idx idx){
    const std::vector<std::string> strList =
    {
        "messageA", "messageB", "messageC"
    };
    return strList[idx];
}

//switch文を使う例
std::string getMessage2(Idx idx){
    std::string str = "";
    switch (idx){
    case a:
        str = "messageA";
        break;
    case b:
        str = "messageB";
        break;
    case c:
        str = "messageC";
        break;
    default:
        break;
    }
    return str;
}


int main(int argc, char* argv[]){
    //enumとvector型による記述の簡略化
    for(int i = 0; i < 3; i++){
        std::cout << i << " " << getMessage(static_cast<Idx>(i)) << std::endl;
    }

    //switch文を使ってstring型を返す関数の例
    for (int i = 0; i < 3; i++){
        std::cout << i << " " << getMessage2(static_cast<Idx>(i)) << std::endl;
    }

    //以下はありがちなCでの記述
    for (int i = 0; i < 3; i++){
        switch (i){
            case a:
                printf("%d messageA\n", i);
                break;
            case b:
                printf("%d messageB\n", i);
                break;
            case c:
                printf("%d messageC\n", i);
                break;
            default:
                break;
        }
    }
}

補足:
上記の例では、

const std::vector<std::string> strList =
    {
        "messageA", "messageB", "messageC"
    };

の部分を、グローバル変数にしてしまって、その配列を直接strList[idx]として参照する方法があるだろう。その場合、配列名とenum型の名称が、その役割にふさわしい名前になっていることがポイントである。

例:データファイルの形式にXML形式を使う。

 OpenCVにもXMLの入出力ができるライブラリがある。
http://opencv.jp/opencv2-x-samples/xml_yaml
画像データだけではなく、一般の多次元の数値データを扱うことができる。そうすることで、保存されたデータの可読性がよい形式で保存でき、保存したデータを読み込む際のパーサーを自分で書く必要もなくなり、メンテナンスすべきコードが大幅に削減される。

例:関数の引数に辞書を使うと、コードの記述が簡潔になる。(python)

言語にもよるけれども、たくさんあるキーワード引数を辞書で受け渡しできると記述が大幅に簡潔になる。Pythonの場合にはそれができる。

def cheeseshop(kind, *arguments, **keywords):

仮引数の最後に **name の形式のものがあると、それまでの仮引数に対応したものを除くすべてのキーワード引数が入った辞書 (マップ型 を参照) を受け取ります。 **name は *name の形式をとる、仮引数のリストを超えた位置指定引数の入ったタプルを受け取る引数 (次の節で述べます) と組み合わせることができます。 (*name は **name より前になければなりません)。

たとえば、pythonのOpenCVでは次のようにかけます。

hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
human, r = hog.detectMultiScale(img, winStride=(8, 8), padding=(32, 32), scale=1.05)

のwinStride=(8, 8), padding=(32, 32), scale=1.05の部分は
辞書で与えることで、記述を簡潔にすることができます。

hogParams = {"winStride":(8, 8), "padding":(32, 32), "scale":1.05}
human, r = hog.detectMultiScale(im, **hogParams)

それらの辞書の記述を1箇所にすることで、検出方式による固有の部分を、ソースコードのあちこちにばらまかなくてよくなります。

numpy,matplotlibよりはpandas

Pythonの数値計算やグラフ作成の環境としては、numpyやmatplotlibがあります。しかし、データとその意味するところが対応関係にないために、グラフを作成するたびに、plt.xlabel("Sepal length"); plt.yabel("Sepal width")とかをそのたびに入力するのが面倒です。
X[:, 0], X[:, 1]
とかいう変数がいったい何者なのか

plt.scatter(X[:, 0], X[:, 1], c=Y, cmap=plt.cm.Paired)
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')

それよりは
pandasのDataFrameで次のように記述できる方が、なんの計算をしているのかもわかりやすいし、
グラフを作図するのも簡潔に書ける。
X['Sepal length'], X['Sepal width']

次のページでは、scikit-learnのデータセットをpandasで利用する方法が書いてある。

scikit-learn に付属しているデータセット

よいデータ構造は、コードを簡潔にします。

まとめ
例:swicth文から配列の参照へ
例:データファイルの形式にXML形式を使う。
例:関数の引数に辞書を使うと、コードの記述が簡潔になる。(python)
例:numpy, matplotlibよりはpandasのデータフレームを使う。(python)

追記:
map型を使うこともコードを簡潔にします。
yamlファイルやJSONファイル形式も便利です。