読者です 読者をやめる 読者になる 読者になる

構造体によるデータ集約

個人的な観測範囲内での話ですが、「プログラマ」と呼ばれる人の中には、自分で構造体 (あるいはクラス) を定義することを面倒臭がり、これをしないで済ませようとする人が少なくありません。 彼らは、本来構造体として集約されるべきデータをプリミティブ (組み込み型) と標準ライブラリなどで与えられたコンテナ型を組み合わせることで表現しようとします。

例えば、地図上の登録地点の情報を CSV (各列にID, 名前, 緯度, 経度, 解説, ... が格納されている) から読み取るコードは以下のようになるわけです。 (csvParseLine は現在開いているCSVファイルから1行分のデータを vector<string> として取ってくる関数だと考えてください。)

class MyApplication {

    //地点データ
    vector<UINT>   m_vID;          //ID
    vector<string> m_vName;        //名前
    vector<double> m_vLatitude;    //緯度
    vector<double> m_vLongitude;   //経度
    vector<string> m_vDescription; //説明

    //地点データの読み込み
    int loadLocationData(){

        m_vID         .clear();
        m_vName       .clear();
        m_vLatitude   .clear();
        m_vLongitude  .clear();
        m_vDescription.clear();

        int nCount =0;

        for (;;){

            vector<string;> row =csvParseLine();
            if (row.size < 5) break;    

            m_vID         .push_back(static_cast<UINT>(atoi(row[0])));
            m_vName       .push_back(row[1]);
            m_vLatitude   .push_back(atof(row[2]));
            m_vLongitude  .push_back(atof(row[3]));
            m_vDescription.push_back(row[4]);

            ++nCount;
        } //for (;;)

        return nCount;
    };
}

だからそうじゃねぇだろうが!!

上の書き方では、フィールドを格納するのに使用されているコンテナ群 (m_vID, m_vName, ... ) の関係がプログラムの構造として明示されなくなってしまいます。 例えば、各コンテナの要素数は常に等しいことが期待されるわけですが、そのチェックも容易ではないでしょう。 更に、地点データとは別のデータを保持する vector メンバが追加された場合、それらの各ベクタの区別はプログラマがコードから (コメントや空行による区切りによって) 読み取らなければなりません。 これはまったく非合理的なやり方です。

ではどうすれば良いのかと言えば、最初に述べた通り、「地点」に関するデータを集約する構造体を作ればよいのです。 構造体は、まさにそうしたデータ集約を行うために考え出された概念・機能なのですから!

//地点データ構造体
typedef struct {
    UINT   uID;         //ID
    string name;        //名前
    double latitude;    //緯度
    double longitude;   //経度
    string description; //説明
} Location;

class MyApplication {

    vector<Location> m_vLocation; //地点データ

    //地点データの読み込み
    int loadLocationData(){

        m_vLocation.clear();

        for (;;){

            vector<string;> row =csvParseLine();
            if (row.size < 5) break;    

            Location l;
            l.uID         =static_cast<UINT>(atoi(row[0]));
            l.name        =row[1];
            l.latitude    =atof(row[2]);
            l.longitude   =atof(row[3]);
            l.description =row[4];

            m_vLocation.push_back(l);
        } for (;;)

        return m_vLocation.size();
    }
};

このように「素直に」コードを書けば、先に挙げたような問題点はすべて解消されます。

担当: 成田 (違うものは区別しよう)