【プログラマー新米の"言語処理100本ノック 2020"】第2章を解く【前半:10~15】

#はじめに
　ネットをさまよっている時にふと、"[言語処理100本ノック 2020](https://nlp100.github.io/ja/)"というサイトに出会いました。自然言語処理を触ってみたいなと思っていた反面、プログラミングは競プロを少しやったプログラマー新米。ちょっと興味もあるのでせっかくなので挑戦してみようと思います。
　この記事を書いている時点では全体の半分しか終わっていませんが、備忘録的な意味で書いて行こうと思います。心が折れたらやめます。先の記事がなかったら察してください。

#環境とスタンス
###環境
+ OS : macOS Catalina 10.15.3
+ Python : 3.7.6

###スタンス
+ 実装はあんまり頑張らない
+ 慣習とか知らない。
+ 安全性もそこまで考えない。
+ 他人が読めるように頑張る。
+ Python初心者にできるだけ優しくかきたい(願望)。

極力解説を書こうと思いますが、気になった方は調べることをお勧めします。

ここまで[前回](https://qiita.com/Uma_cryptids/items/c004a74004f450da675a)と一緒。

#"第2章: UNIXコマンド"を解く
以下引用は[こちら](https://nlp100.github.io/ja/ch02.html)から

>popular-names.txtは，アメリカで生まれた赤ちゃんの「名前」「性別」「人数」「年」をタブ区切り形式で格納したファイルである．以下の処理を行うプログラムを作成し，popular-names.txtを入力ファイルとして実行せよ．さらに，同様の処理をUNIXコマンドでも実行し，プログラムの実行結果を確認せよ．

同様の処理をUNIXコマンドでするのが面倒なのでないです。(それでいいのか)

###10. 行数のカウント
>行数をカウントせよ．確認にはwcコマンドを用いよ．

```python:10.py
with open("popular-names.txt") as f:
    print(len(f.readlines()))
```
```:端末
2780
```
`with open() as ~`は`open()`を単体で使うのと違って`close()`を使う必要がありません。
インデントが終わると勝手に閉じてくれます。
`readlines()`はファイル全体を改行を区切りにしたリストとして返してくれる関数です。

###11. タブをスペースに置換
>タブ1文字につきスペース1文字に置換せよ．確認にはsedコマンド，trコマンド，もしくはexpandコマンドを用いよ．

```python:11.py
from functools import reduce

with open("popular-names.txt") as f:
    print(reduce(lambda a, b: (a+b).replace("\t", " "), f.readlines()))
```
```python:端末
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
Elizabeth F 1939 1880
・
・
```
コードゴルフはまだ続く...(努力の無駄使い)。めちゃくちゃ結果が長いので冒頭だけです。
`reduce()`は`map()`と同じように高階関数です。
iterableなものに対して関数を適応できます。総和を求める時なんかに便利な関数です。

###12. 1列目をcol1.txtに，2列目をcol2.txtに保存
```python:12.py
with open("popular-names.txt") as a,\
        open("col1.txt", mode="w") as b,\
        open("col2.txt", mode="w") as c:
    for l in a.readlines():
        x, y, *z = l.split("\t")
        b.write(x+"\n")
        c.write(y+"\n")
```
```:col1.txt
Mary
Anna
Emma
Elizabeth
・
・
```
```:col2.txt
F
F
F
F
・
・
```
`with open()`は複数つなげることができます。横に長くなりそうだったので`\`を使って改行しています。
`x,y,*z=`は、返り値の一つ目が`x`に二つ目が`y`に後の残りが`z`に入ります。
あとは、必要なものをファイルに書き込んでいるだけです。

###13. col1.txtとcol2.txtをマージ
>12で作ったcol1.txtとcol2.txtを結合し，元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ．確認にはpasteコマンドを用いよ．

```python:13.py
with open("marge.txt", mode="w") as a,\
        open("col1.txt") as b,\
        open("col2.txt") as c:
    for x, y in zip(b.readlines(), c.readlines()):
        a.write(x[:-1]+" "+y)
```
```:marge.txt
Mary F
Anna F
Emma F
Elizabeth F
・
・
```
`zip()`は複数のリストの要素をまとめて取得することができる関数です。
両方ともの要素の最後に改行があるので`x`の方は最後の文字を取り除いています。

(だんだん書くことがなくなってきたぞ...)

###14. 先頭からN行を出力
>自然数Nをコマンドライン引数などの手段で受け取り，入力のうち先頭のN行だけを表示せよ．確認にはheadコマンドを用いよ．

```python:14.py
import sys
from functools import reduce

with open(sys.argv[2]) as f:
    S = f.readlines()
    print(reduce(lambda a, b: a+b, S[:min(len(S), int(sys.argv[1]))]),
          end="")
```

いやー...`reduce()`って便利ですね...。
`sys.argv`は"ファイル名.py"を含むコマンドラインに入力された文字列を格納しています。
これを使うとコマンドライン引数を使うことができます。


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

```python:15.py
import sys
from functools import reduce

with open(sys.argv[2]) as f:
    S = f.readlines()
    print(reduce(lambda a, b: a+b, S[max(0, len(S)-int(sys.argv[1])):]),
          end="")
```

14問目でしたことの再放送です。
ファイルの行数より多い要求がくると困るので、`max()`を使ってオーバーしないようにしています。

#おわりに
今回はネタ(面白いとは言ってない)少なめでしたがいかがだったでしょうか。解説は多分増えました。
Theがつくほどテキトーになってきましたが、これが言語処理100本ノックの一つの答えとなれば幸いです。結構、これに関する記事は多いので気になる方は見てみてください。

それでは皆さん次の記事、第2章後編でお会いしましょう。
コードの短縮アイデアなどなどあればぜひコメントください。


それでは。