Project Euler – 問題17

1から5までの数を単語にするとone,two,three,four,fiveとなり、全部で 3+3+5+4+4=19文字になる。

1から1000までの全ての数を単語にすると何文字になるか求めよ。

(注)スペースやハイフンは数に含めない。例えば342(three hundred and forty-two)は23文字、115(one hundred and fifteen)は20文字である。andの使 い方はイギリス方式である。

題意のとおりに、1から1000までの数を単語であらわしたものを求め、その文字 数の和を求めることにする。数を単語であらわす関数inwordsを以下のように定 義する。20以下の場合は単語のリストから単語を取り出す。20以上の場合は10 の位と1の位をそれぞれリストから求める。100以上になるとさらに100の位を別 に求めて連結する。問題自体は決して難しくないが、ネイティブでない場合、 数の英語表記を間違えないように注意する必要がある。とくにつづりを間違え ただけでも答えを間違ってしまう。100の位とその下の間にandをつけるがちょ うど100の時にはつけないなども注意が必要だ。

euler017 = sum $ map (length . inwords) [1..1000]
    where words1  = [ "",
                      "one",
                      "two",
                      "three",
                      "four",
                      "five",
                      "six",
                      "seven",
                      "eight",
                      "nine",
                      "ten",
                      "eleven",
                      "twelve",
                      "thirteen",
                      "fourteen",
                      "fifteen",
                      "sixteen",
                      "seventeen",
                      "eighteen",
                      "nineteen"]
          words10 = [ "",
                      "",
                      "twenty",
                      "thirty",
                      "forty",
                      "fifty",
                      "sixty",
                      "seventy",
                      "eighty",
                      "ninety" ]
          inwords n
              | n < 20 = words1 !! n
              | n >= 20 && n < 100 = let (d, m) = divMod n 10
                                     in words10 !! d ++ inwords m
              | n >= 100 && n < 1000 = let (d, m) = divMod n 100
                                           lower  = if m /= 0
                                                    then "and" ++ inwords m
                                                    else ""
                                       in inwords d ++ "hundred" ++ lower
              | n == 1000 = "onethousand"