Speech Module (Check iO > Home)
どんな問題?
長々とストーリーが書いてるけど、英語苦手なのでスルー。ようするに、数字を英語に変換するだけの問題だ。
数字は 0 < N < 1000 の整数のみ。単語と単語の間にはスペース1個が必要。
例はこちら
checkio(4)=='four' checkio(143)=='one hundred forty three' checkio(12)=='twelve' checkio(101)=='one hundred one' checkio(212)=='two hundred twelve' checkio(40)=='forty'
どうやって解く?
まずは英語での数え方を思い出さないと・・・。
1~12 は、one two three ... twelve と固有の呼び方。
13~19 は、○○teen。
20 は、twenty
21~29は、twenty + one ~ nine
30~99 は、同じような感じで続く。
100 は one hundred
あれ、200って、two hundredだっけ?two hundredsだっけ?
ググってみた。two hundred でいいみたい。「幾百人」とかは、"hundreds of people"のように複数形になると、、、そういえば習ったっけ。というか、上の例に載ってたわ。
あれ、上の例で、143 が one hundred forty three となってるけど、 one hundred and forty three じゃないのか?
ググってみた。米口語ではandを略すこともある、だってさ。知らんわ。
英語嫌いとしては、だんだんイライラしてきたよ。
解法は簡単。まず、100未満と100以上でわける。
100未満については、1~20は辞書を定義しておく。21~99は、'twenty'~'ninety' + 'one'~'nine' で作成できる。
100以上の数字は、まず100の桁を 'one'~'nine' + 'hundred' で作成できるので、100未満の部分を足せば良い。
では、Solve it!ボタンをクリック!
あれ、すでに、あらかじめ必要な単語が定義されているじゃないか。
FIRST_TEN = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] SECOND_TEN = ["ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"] OTHER_TENS = ["twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"] HUNDRED = "hundred"
じゃあ、あとは処理を書くだけ。
def checkio(number): def under100(n): if n <= 9: return FIRST_TEN[n - 1] elif n <= 19: return SECOND_TEN[n - 10] else: s = OTHER_TENS[n // 10 - 2] if n % 10 > 0: s += ' ' + FIRST_TEN[n % 10 - 1] return s if number < 100: return under100(number) else: return FIRST_TEN[number // 100 - 1] + ' ' + HUNDRED + ' ' + under100(number % 100)
100未満の処理を under100()にまとめて、100未満と100以上で分岐。さくっと完成、Run&Check!
FAIL
Your result: "one hundred nine"
Right result: "one hundred"
Fail: checkio(100)
えっ?
あー、そうか、numberが100のときは、under100に0が渡されるけど、引数が0のときを想定してなかった。FIRST_TEN[-1]で'nine'が返ってしまったらしい。
Pythonのリストでは lst[-n] で後ろからn番目の要素を返すため、範囲外エラーにはならない。この機能って便利なんだけど、他の言語(C#)から来た人は、エラーの出ないバグに悩まされる原因にもなる。
もう一つ、CheckiOって、あらかじめ__main__節に書いてあるものだけテストしてると思ってた。checkio(100) は書いてないけど、ちゃんとテストしてるんだね。
if __name__ == '__main__': assert checkio(4) == 'four', "1st example" assert checkio(133) == 'one hundred thirty three', "2nd example" assert checkio(12) == 'twelve', "3rd example" assert checkio(101) == 'one hundred one', "4th example" assert checkio(212) == 'two hundred twelve', "5th example" assert checkio(40) == 'forty', "6th example"
というわけで、初めてのFAILである。バッジはもらえないのかな?
numberが100以上の場合は、 number % 100 > 0 のときだけ、under100()を呼び出して追加するように修正した。
ついでに、under100() にnの範囲チェック(assert)を入れておいた。
def under100(n): assert 1 <= n <= 99 if n <= 9: return FIRST_TEN[n - 1] elif n <= 19: return SECOND_TEN[n - 10] else: s = OTHER_TENS[n // 10 - 2] if n % 10 > 0: s += ' ' + FIRST_TEN[n % 10 - 1] return s if number < 100: return under100(number) else: s = FIRST_TEN[number // 100 - 1] + ' ' + HUNDRED if number % 100 > 0: s += ' ' + under100(number % 100) return s
これでどやっ!
Task solved!
この方法だと、最後から2行目を
s += ' and ' + under100(number % 100)
に書き換えるだけで、one hundred and forty three 形式にできる。
クライアントが急に「普通、hundred の後に and 入れるよね?だよね?」なんて言い出しても安心だ。