Google code jam Africa 2010 qualification round B. reverse wordsに挑戦する.
この問題を入力と出力で定義すると,
である.
入力と,それに対する正しい出力の例は以下の通りである.
ボトムアップ形式で関数を作る.
まず入力$S$が与えられたら,正しい出力を返す関数を定義する.
以下の関数answerはその一例である.
def answer(S):
S = S.split()
S.reverse()
return S
上記の例で,合っているか試してみる.
answer('this is a test')
['test', 'a', 'is', 'this']
answer('foobar')
['foobar']
answer('all your base')
['base', 'your', 'all']
良いようだ.
次に,入力データが文字列としてそのまま書き込まれたデータファイルを読み込んで,指定された形式の文字列で解答を書き出す関数all_answerを作ってみる.
準備として,文字列に関する関数とメソッドを確認する.
組み込み関数strを使うと,整数・少数・リストなどを文字列に変換できる.
str(1)
'1'
str(3.14)
'3.14'
str([2, '3', 'miya'])
"[2, '3', 'miya']"
文字列のメソッドjoinを使うと,リストを連結した文字列を作れる. sを文字列とすると
s.join(リスト)
で,間にsを挟んでリストを連結した文字列を作れる.
'miya'.join(['3', '2', '4'])
'3miya2miya4'
joinを使う場合には,リストの要素は全て文字列でないといけない.
準備として,もう1つ組み込み関数を紹介する.
組み込み関数intを使うと,文字列を整数に変換できる.
int('123')
123
int('012\n')
12
組み込み関数intは,先頭に0が入っていたり末尾に空白文字や改行文字が入っていても,整数と解釈できるならば整数に変換してくれる.
int('3.14')
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-11-1456603af047> in <module> ----> 1 int('3.14') ValueError: invalid literal for int() with base 10: '3.14'
組み込み関数intは,整数と解釈できる文字列しか整数に変換してくれない.
以上の準備のもとに,入力データが文字列としてそのまま書き込まれたファイルを読み込んで,指定された形式の文字列で解答を書き出す関数answerを以下に定義する. もちろんこれもあくまでも一例である.
いきなり下記のすべてを真似るとわけがわからなくなると思うので,段階的に試すと良いだろう.
def all_answer(input_file_name, output_file_name):
input_file = open(input_file_name) # 入力データファイルを開いて
input_data = input_file.readlines() # 全行をリストとして読み込んで
input_file.close() # 入力データファイルを閉じる.
output_file = open(output_file_name, 'w') # 出力データファイルを開いて
N = input_data[0] # 入力データの最初の行をNに代入する.
input_data = input_data[1:] # 入力データの最初の行を消す.
# これでinput_data[0]が「次の行」となる.
N = int(N) # ファイルから読み込んだままだと文字列なので,整数に変換する.
# intは文字列を整数に変換する組み込み関数である.
# intは,引数の文字列に改行文字が入っていても,整数に変換してくれる.
for n in range(N):
S = input_data[0] # 「次の行」をSに代入する.
input_data = input_data[1:] # これでinput_data[0]が「次の行」になる.
S = S.rstrip() # ファイルから読み込んだままだと末尾に改行文字が入っているので,それを消す.
R = answer(S) # ここで問題を本質的に解く.
output_file.write('Case #' + str(n+1) + ': ' + ' '.join(R) + '\n')
output_file.close() # 出力データファイルを閉じる.
return # 必要ないが,行儀よく書いておく.
データが書き込まれているテキストファイルB-sample.inを用意して,それを読み込み,B-sample.outというファイルに出力を書き込んでみる.
all_answer('B-sample.in', 'B-sample.out')
望みどおりの文字列がB-sample.outに書き出されているか,テキストエディターで見てみよう.
上記の自作関数all_answerは正しく解答を出力する. しかし,簡単な問題を解いている割にはコードが長い.
コードが長い理由の1つはリストの扱いが下手だからである. リストを扱う便利なメソッドを駆使するとコードはもっと簡潔になる.
以下では,リストのメソッドpopを紹介し,上記のanswerをもう少し簡潔にする.
まずは,popの使用例を以下に記す.
x = [3, 1, 4, 2]
x
[3, 1, 4, 2]
y = x.pop()
x
[3, 1, 4]
y
2
この例からわかるように,
リスト.pop()
でリストから最後の要素を取り出せる. 取り出された要素はリストから消える.
最後以外の要素を取り出したい場合には,popの引数に,リストの要素の添字を入れる.
x = [3, 1, 4, 2]
y = x.pop(2)
x
[3, 1, 2]
y
4
このpopを使えば,上記all_answerの
N = input_data[0]
N = int(N)
input_data = input_data[1:]
は
N = input_data.pop(0)
N = int(N)
とできる.
さらにこれを1つにまとめて
N = int(input_data.pop(0))
としても良い.
これを踏まえて先程のanswerを再定義すると以下の通りとなる. 実質的には全く同じ処理となるので,コメントは省く.
def all_answer(input_file_name, output_file_name):
input_file = open(input_file_name)
input_data = input_file.readlines()
input_file.close()
output_file = open(output_file_name, 'w')
N = int(input_data.pop(0)) # 以前は3行だった命令が1行になった.
for n in range(N):
S = input_data.pop(0) # 以前は3行だった命令が1行になった.
S = S.rstrip() # この命令も直前の命令と合わせて1つにできるが,ここでは分けておく.
R = answer(S)
output_file.write('Case #' + str(n+1) + ': ' + ' '.join(R) + '\n')
output_file.close()
return
念のため,動作を確認する.
all_answer('B-sample.in', 'B-sample.out')
再び,望みどおりの文字列がB-sample.outに書き出されているか,テキストエディターで見てみよう.
関数all_answerはもっと簡潔に書けるが,今回はここまでにしておく.