エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

プログラミングの攻めと守り

周回遅れ。すでに第9回の参考問題が出題されているという状況で第8回の参考問題を解くという。


以下、コード。コードの全体はGitHubに格納しています。

#include <iostream>
#include <sstream>
#include <string>

static const std::string NIBBLES[] =
{
    "0000", "1000", "0100", "1100", "0010", "1010", "0110", "1110",
    "0001", "1001", "0101", "1101", "0011", "1011", "0111", "1111"
};

static const std::string HEXES = "0123456789abcdef";

std::string solve(const std::string& input)
{
    std::string bits;
    for(std::string::const_iterator i = input.begin(); i != input.end(); ++i)
    {
        bits += NIBBLES[HEXES.find(*i)];
    }

    std::string::size_type pos = 0;
    std::string            result;
    for(;;)
    {
        if     (bits.size() <= pos)               { pos = std::string::npos; break; }
        else if(bits.substr(pos, 3) == "000"    ) { result += 't'; pos += 3; }
        else if(bits.substr(pos, 4) == "0010"   ) { result += 's'; pos += 4; }
        else if(bits.substr(pos, 4) == "0011"   ) { result += 'n'; pos += 4; }
        else if(bits.substr(pos, 4) == "0100"   ) { result += 'i'; pos += 4; }
        else if(bits.substr(pos, 5) == "01010"  ) { result += 'd'; pos += 5; }
        else if(bits.substr(pos, 7) == "0101101") { result += 'c'; pos += 7; }
        else if(bits.substr(pos, 6) == "010111" ) { result += 'l'; pos += 6; }
        else if(bits.substr(pos, 4) == "0110"   ) { result += 'o'; pos += 4; }
        else if(bits.substr(pos, 4) == "0111"   ) { result += 'a'; pos += 4; }
        else if(bits.substr(pos, 2) == "10"     ) { result += 'e'; pos += 2; }
        else if(bits.substr(pos, 4) == "1100"   ) { result += 'r'; pos += 4; }
        else if(bits.substr(pos, 4) == "1101"   ) { result += 'h'; pos += 4; }
        else if(bits.substr(pos, 3) == "111"    ) { pos += 3; break; }
        else                                      { pos = std::string::npos; break; }
    }

    if(pos != std::string::npos)
    {
        std::ostringstream output;
        output << result << ":" << pos;
        return output.str();
    }
    else
    {
        return "*invalid*";
    }
}

// テストコード略

攻めと 守りーを くりかえーし

「どう書く」はとても楽しいのですが(出題されている鍋谷さんに感謝)、ハコニワ的なところがあって、枠をはみ出す心配がないのでアイディアをガシガシとプログラミングを進めてしまう、という側面があります。それ自体は悪いわけではないのですが、プログラミングではほとんどのばあいは耐性のあるコードを書かなければなりません。

たとえば。今回こういうコードを書きました。

        bits += NIBBLES[HEXES.find(*i)];

今回の「どう書く」のルール上はこれで問題ありませんが、find関数はstd::string::nposを返す可能性があります。引数にstd::string::nposがあたえられたばあいのstd::string::operator []の動作は未定義なので、この参考問題以外の場所でこのコードが使われたら原因不明のバグになりかねません。


個人的には、いまのところ仕事でコードを書く機会は少なくもっぱら読むばかり。ですが、コードを読んでいると耐性=守りを考えなかったコードが原因のバグによく遭遇します。

「どう書く」は攻めるプログラミングの訓練に最適だと思います。でも「どう書く」で攻めをおぼえたら、今度は守りをおぼえないとならない。もちろん「どう書く」がよくないというわけではありません。「どう書く」でお膳立てされた環境でプログラミングするだけで満足しちゃだめだってことです。自戒を込めて。

いつか読むはずっと読まない:ジョブチェンジ

なんで急にそんなことを言い出したかというと。現在「体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践」で勉強中なためでした。なんで急に勉強しているかというと。それについては、またいずれ。