Project Eueler: problem17
問題
If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total.
If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?
NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of "and" when writing out numbers is in compliance with British usage.
回答
ここを参考にまずRSpecでテストを作ってみた
http://d.hatena.ne.jp/myokoym/20100919/1284887970
require "p17.rb" describe "#toEng" do it "1 to one" do toEng(1).should == "one" end it "14 to fourteen" do toEng(14).should == "fourteen" end #略 it "999" do toEng(999).should == "nine hundred and ninety-nine" end it "1000" do toEng(1000).should == "one thousand" end end describe "#sumLetters" do it "342" do sumLetters(toEng(342)) == 23 end it "115" do sumLetters(toEng(115)) == 20 end it "999" do sumLetters(toEng(999)) == 25 end end describe "#sumTotal" do it "5" do sumTotal(5).should == 19 end it "1000" do sumTotal(1000).should == 21124 end end
回答はこうなった。
$data = <<"NUMDATA" 0,zero 1,one 2,two 3,three 4,four 5,five 6,six 7,seven 8,eight 9,nine 10,ten 11,eleven 12,twelve 13,thirteen 14,fourteen 15,fifteen 16,sixteen 17,seventeen 18,eighteen 19,nineteen 20,twenty 30,thirty 40,forty 50,fifty 60,sixty 70,seventy 80,eighty 90,ninety NUMDATA $dic = Hash.new def makedic() $data.each do |line| ar = line.split(/,/) $dic[ar[0].to_i] = ar[1].chomp! end end def toEng(num) tar = num.to_s.split(//) str = "" if tar[-4] != nil return "one thousand" end if tar[-3] != nil str += $dic[tar[-3].to_i] str += " hundred" str += " and " unless num % 100 == 0 end if tar[-2] == "0" || tar[-2] == nil unless tar[-1] == "0" str += $dic[tar[-1].to_i] end elsif tar[-2] == "1" str += $dic[(tar[-2]+tar[-1]).to_i] else str += $dic[(tar[-2]+"0").to_i] unless tar[-1] == "0" str += "-" + $dic[tar[-1].to_i] end end return str end def sumLetters(str) str.gsub("-"," ").split(/ /).inject(0){|sum,s| sum + s.size} end def sumTotal(upper) makedic() 1.upto(upper).inject(0){|sum,x| sum + sumLetters(toEng(x))} end p sumTotal(1000)
備考
リファクタリングにはテスト有効。
テスト駆動で書く習慣をつけよう。