Clojure入門 - Project Eulerを解いてみる 問17
1から1000までの数を英語で書き出した時に使う文字数を算出。
これが結構めんどくさかった。
「数字 -> 文字列 -> 数字」と変換していく。「数字 -> 数字」でもあり得るのだが、一旦文字列に変換した方がエラーの発見が容易そうなので。
というわけで、まずは数字を桁ごとにわける。
(defn break-num [num] (->> num (format "%04d") (map int) (map #(- % 48))))
(int 1)
が49なのが少々めんどくさい・・・
次は単語の定義。ここは非常にめんどくさい・・・
(def unit-words {0 "" 1 "one" 2 "two" 3 "three" 4 "four" 5 "five" 6 "six" 7 "seven" 8 "eight" 9 "nine"}) (def ten-nineteen-words {0 "ten" 1 "eleven" 2 "twelve" 3 "thirteen" 4 "fourteen" 5 "fifteen" 6 "sixteen" 7 "seventeen" 8 "eighteen" 9 "nineteen"}) (def twenty-words {2 "twenty" 3 "thirty" 4 "forty" 5 "fifty" 6 "sixty" 7 "seventy" 8 "eighty" 9 "ninety"}) (def hundred-word "hundred") (def thousand-word "thousand") (def and-word "and")
11から19が特殊だったり、20〜90が別の単語になっていたり、めんどくさい。こういう時に日本語・中国語の数の論理性がわかる。まあフランス語じゃなくてよかった。なんだquatre-vingt dix-neufって。
そういった関係をコードに落とし込んで関数化する。
(defn num-to-word [n] (let [[thousands hundreds tens units] (break-num n) thousand-str (if (pos? thousands) (str (unit-words thousands) thousand-word)) hundred-str (if (pos? hundreds) (str (unit-words hundreds) hundred-word)) and-str (if (and (pos? (+ thousands hundreds)) (pos? (+ tens units))) and-word) tens-units-str (cond (zero? tens) (unit-words units) (= 1 tens) (ten-nineteen-words units) :else (str (twenty-words tens) (unit-words units)))] (str thousand-str hundred-str and-str tens-units-str)))
あとは組み合わせるだけ。
(->> (range 1000) (map inc) (map num-to-word) (map count) (apply +))
しかし、一番大変だったのは変数・関数につける名前をどうするか、だった。もうちょっとなんとかなった気がする。