Python で言語処理100本ノック2015

プログラムとPythonのお勉強のために「言語処理100本ノック 2015」をやるよ！
http://www.cl.ecei.tohoku.ac.jp/nlp100/

とりあえず順次更新していこうと思ってます。（結構ゆっくりになるかも・・・）
GitHubにも順次上げていこうと思います。

- [tanaka0325/nlp100](https://github.com/tanaka0325/nlp100)

## ルール

まずは自分で書いたコードを載せる。
そのあとに調べてみてもっと良いと思った書き方を載せて行こうと思います。

## 第1章: 準備運動

### 00. 文字列の逆順

>文字列"stressed"の文字を逆に（末尾から先頭に向かって）並べた文字列を得よ．

#### 自分のコード

短い書き方があるんだと思うのだけど、
経験が浅すぎて思いつかなかったので、すごい泥臭いやり方に・・・

```py3:knock000.py
s = "stressed"

def reverse(s: str) -> str:
	list = []

	for char in s:
		list.insert(0, char)

	return "".join(list)

print(reverse(s))
```

#### 調べたコード

```py3:
s = "stressed"
print(s[::-1])
```

こんなに短く書けるみたい。。。

文字列のスライスのおさらい

```py3:
str = "I have a dream!"

# 基本形 str[最初:最後]
str[0:6]
#=> 'I have'

# 指定は省略することも可能 （省略した場合、全部、というか終端？まで）
str[:10]
#=> 'I have a d'

str[3:]
#=> 'ave a dream!'

# ステップ数を指定することもできる str[最初:最後:ステップ数]
str[0:12:3]
#=> 'Ia d'
```

なので

```py3:
s[::-1]
```
これは「最初から最後までを反対から1つずつ」＝逆順、的な意味になる

### 01. 「パタトクカシーー」

>「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ．

#### 自分のコード

数値を直指定になっちゃってるけど

```py3:knock001.py
s = "パタトクカシーー"
print(s[0] + s[2] + s[4] + s[6])
```

全部偶数なので、ループで回して偶数だったら、的な書き方をするのが正解なのかな？？

#### 調べたコード

今回はなし

追記：
コメントでご指摘頂いたので追記します。
00.と同じようにステップ数を指定してスライスで下記のように書ける

```py3:
s = "パタトクカシーー"
print(s[::2])
```

### 02. 「パトカー」＋「タクシー」＝「パタトクカシーー」

> 「パトカー」＋「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ．

#### 自分のコード

```py3:knock002.py
s1 = "パトカー"
s2 = "タクシー"
s3 = ""

for i in range(len(s1)):
    s3 = s3 + s1[i] + s2[i]

print(s3)
```

rangeの引数の len(s1) ってところがイケてない気がする。
与えられた文字列が同一文字数だから大丈夫なだけ感。

#### 調べたコード

今回はなし
（なんかこのやり方よくない気がして来たので、調べたものがある場合のみこの項目を設けることにします。）

追記：
len(s1)のところzip関数を使えばいけるらしい

```py3:
for char1, char2 in zip(s1, s2):
    s3 = s3 + char1 + char2
```

zip関数は複数の引数を同時にループで回して処理してくれるやつ

追記の追記：
コメントでご指摘を頂いた。

```py3:
for char1, char2 in zip(s1, s2):
    s3 = s3 + char1 + char2
```

上のように文字列をつなげていると、ループ回るごとに毎回新しいメモリを確保するので遅くなる。
なので、文字列のリストを作ってから、最後にjoinでがっちゃんこするのが良いみたいです

以下教えて頂いたもの

```py3:
print(''.join([char1 + char2 for char1, char2 in zip(s1, s2)]))
```


### 03. 円周率

#### 自分のコード

>"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し，各単語の（アルファベットの）文字数を先頭から出現順に並べたリストを作成せよ．

```py3:
s = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."

words = s.split(" ")
char_count = []

for word in words:
    char_count.append(len(word))

print(char_count)
```

追記：
コメントでご指摘を頂いた。
リスト内包表記を使えれば簡潔に書けるようです
そしてsplit関数はデフォルトが空白なので、いちいち空白は指定しなくて良いそうです。
さらに、,や.も削らないといけないみたいです。。。

以下コメントで教えて頂いたものを記載します
ただ、python3.4ではtranslateあたりの仕様が変わっていたみたいなので、下記のように書き換えた

```py3:
s = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."

print([len(word.translate(word.maketrans({".":None,",":None}))) for word in s.split()])
```

なんかうまく書けないので、別の方法としては正規表現を仕様して置換（今回は削除）するパターンもある

```py3:
import re

s = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."

pat = re.compile('[.,]')
print([len(pat.sub('', word)) for word in s.split()])
```


### 04. 元素記号

>"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し，1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字，それ以外の単語は先頭に2文字を取り出し，取り出した文字列から単語の位置（先頭から何番目の単語か）への連想配列（辞書型もしくはマップ型）を作成せよ．

#### 自分のコード

```py3:
s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."

s = s.replace(".", "")
words = s.split(" ")
words_index = {}

for i, word in enumerate(words):
    if i in [1, 5, 6, 7, 8, 9, 15, 16, 19]:
        words_index[word[:1]] = i
    else:
        words_index[word[:2]] = i

print(words_index)
```

追記：
コメントで間違ってるとの指摘を頂きました

以下修正版

```py3:
s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."

s = s.replace(".", "")
words = s.split(" ")
words_index = {}

for i, word in enumerate(words):
    n = i + 1
    if n in [1, 5, 6, 7, 8, 9, 15, 16, 19]:
        words_index[word[:1]] = n
    else:
        words_index[word[:2]] = n

print(words_index)
```

あとはforの中の代入のところが冗長なので、コメントで頂いた下記の書き方のほうが簡潔

```py3:
for i, word in enumerate(words):
    n = i + 1
    l = 1 if n in (1, 5, 6, 7, 8, 9, 15, 16, 19) else 2
    words_index[word[:l]] = n
```