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

Arantium Maestum

プログラミング、囲碁、読書の話題

Clojure入門 - Project Eulerを解いてみる 問17続

この記事の続き。

nfunatoさんとあやぴーさんに教えていただき、cl-formatというCommon Lispから受け継いだ関数で数字から英語に変換することが可能なことがわかった。

しかしcl-formatだとアメリカ英語の綴りになり、Project Eulerの求めているのはイギリス式なので、残念ながら使わずにいたのだが。

考えてみたら100000未満の数字だけなら結構簡単にアメリカ式からイギリス式に変換できることに気づいたのでやってみる。

唯一の違いはイギリス式だと100以上の桁と100未満の桁のどちらも0でなければ、両者をandで区切る必要がある、ということだけ。

なのでイギリス式だと1107はone thousand, one hundred and sevenなのに対してアメリカ式だとone thousand, one hundred sevenになる。

そして1000の桁、100の桁は単語数が偶数、100未満の数は必ず1単語で表される。

なのでアメリカ式に書くと3単語以上でなおかつ単語数が奇数の場合、最後の単語の前にandを挿入してやればいい。

(defn american->british [s]
  (letfn [(insert-and [words]
            (if (and 
                  (odd? (count words))
                  (<= 3 (count words)))
              (concat (drop-last words) 
                      ["and" (last words)])
              words))]          
    (as-> s x
          (clojure.string/split x #" ")
          (insert-and x)
          (interpose " " x)
          (apply str x))))

これでアメリカ式の数字の表現からイギリス式に変換できる。

あとはアルファベット以外の文字を消して数えるだけ。

(defn remove-non-alphabet [s]
  (.replaceAll s "[^a-z]" ""))

(->> (range 1 1001)
     (map #(clojure.pprint/cl-format nil "~R" %))
     (map american->british)
     (map remove-non-alphabet)
     (map count)
     (apply +))

これで前回と同じ結果が出る。

ちなみにこれは1~1000だからうまくいく戦略で、例えば100001などはone hundred thousand, oneとなり1000以上の桁を3単語で表すことになるのでダメ。100000以上でもやりようはあるが面倒くさそう。