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

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

ローマ数字パーサ、の下書き

本当は正規表現を使うつもりだったものが、どうもうまくいかないので、自力でパーサを書くことにした。で、そのアルゴリズム検証のための下書き。
なんのかんの言ってもC++が一番なじんでいるので、C++で書いた。

一応意図どおりに動いているようなので、これを翻訳して使う予定。それはまた明日にでも…。

#include <iostream>
#include <algorithm>
#include <string>

template<typename Iterator>
std::pair<Iterator, int> asRomanNumeralDigit(Iterator begin, Iterator end, char ten, char five, char one)
{
    Iterator i = begin;

    if((i != end) && (*i == one))
    {
        ++i;
        if((i != end) && (*i == five))
        {
            ++i;
            return std::make_pair(i, 4);
        }
        else if((i != end) && (*i == ten))
        {
            ++i;
            return std::make_pair(i, 9);
        }
        else
        {
            int value = 1;
            while((value < 3) && (i != end) && (*i == one))
            {
                ++value;
                ++i;
            }
            return std::make_pair(i, value);
        }
    }
    else if((i != end) && (*i == five))
    {
        ++i;
        int value = 5;
        while((value < 8) && (i != end) && (*i == one))
        {
            ++value;
            ++i;
        }
        return std::make_pair(i, value);
    }
    else
    {
        return std::make_pair(i, 0);
    }
}

template<typename Iterator>
int asRomanNumeral(Iterator begin, Iterator end)
{
    static const char M = 'M';
    static const char D = 'D';
    static const char C = 'C';
    static const char L = 'L';
    static const char X = 'X';
    static const char V = 'V';
    static const char I = 'I';

    Iterator itr = begin;
    int      m   = 0;
    while((m < 3) && (itr != end) && (*itr == M))
    {
        ++m;
        ++itr;
    }
    std::pair<Iterator, int> c = asRomanNumeralDigit(itr,     end, M, D, C);
    std::pair<Iterator, int> x = asRomanNumeralDigit(c.first, end, C, L, X);
    std::pair<Iterator, int> i = asRomanNumeralDigit(x.first, end, X, V, I);

    if(i.first != end)
    {
        return 0;
    }

    return m * 1000 + c.second * 100 + x.second * 10 + i.second;
}

int main(int argc, char* argv[])
{
    for(int i = 1; i < argc; ++i)
    {
        std::cout << argv[i] << " => ";
        int result = asRomanNumeral(argv[i], argv[i] + std::strlen(argv[i]));
        if(result != 0)
        {
            std::cout << result << "\n";
        }
        else
        {
            std::cout << "cannot express\n";
        }
    }

    return 0;
}


実行結果。

$ ./RomanNumeral I II III IV V VI VII VIII IX X L C D M IIII IIX MCMLXXIV MDCCCLXXXVIII MDCCCXXXIIII MMMM MMMCMXCIX
I => 1
II => 2
III => 3
IV => 4
V => 5
VI => 6
VII => 7
VIII => 8
IX => 9
X => 10
L => 50
C => 100
D => 500
M => 1000
IIII => cannot express
IIX => cannot express
MCMLXXIV => 1974
MDCCCLXXXVIII => 1888
MDCCCXXXIIII => cannot express
MMMM => cannot express
MMMCMXCIX => 3999