2018年02月20日

言語処理100本ノックでPython入門 #15 - None

  
言語処理100本ノック 2015の問題15に挑戦です。

■ 問題

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

15. 末尾のN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.


■ Pythonのコード
import sys
def tail(fin, fout, count): lines = fin.readlines() fout.writelines(lines[-count:])
def main(): n = int(sys.argv[1]) with open('hightemp.txt', 'r', encoding='utf8') as f: tail(f, sys.stdout, n)
if __name__ == '__main__': main()
これまでの知識を使えば出来ますが、たぶん、上のコードは入力ファイルをすべてメモリにため込むことになるので、 メモリを圧迫するような巨大なテキストファイルだと、この実装は好ましくないです。

なのでN行分だけをリストにため込むコードも書いてみました。
def tail(fin, fout, count):
    buff = [None] * count
    i = 0
    for line in fin:
        buff[i] = line
        i = i + 1 if i + 1 < count else 0
    for _ in range(count):
        if buff[i] != None:
            fout.write(buff[i])
        i = i + 1 if i + 1 < count else 0

5行しかないテキストファイルに対して、10行を出力するような指定をされた場合にもうまく動作するようにしたので、 ちょっとコードがごちゃごちゃしてしまいました。
RingBufferのようなクラスがあれば、もっと簡単に書けるのだけど... まあ、良しとしよう。


■ 今回のトピック

サイズを指定してリストの確保

これは、そういった構文は用意されていないようです。 ただし、次のように書けば、5個分の要素を初期値指定で確保できます。
buff = [1] * 5
buffの値は、
[1, 1, 1, 1, 1]
となります。


None

Noneは値の非存在を示します。C#でいうところのnullと同様の目的で使えそう。
buff = [None] * count
で、リストを初期化しています。
それと、Noneの判定は、is または、is not を使うみたいです。なので、
if buff[i] != None:
ではなく、
if buff[i] is not None:
のほうが良いらしいです。

■ 結果
$ python nlp15.py 5

埼玉県  鳩山    39.9    1997-07-05
大阪府  豊中    39.9    1994-08-08
山梨県  大月    39.9    1990-07-19
山形県  鶴岡    39.9    1978-08-03
愛知県  名古屋  39.9    1942-08-02