言語処理100本ノックをPython3で解く「第１章 準備運動」

##はじめに

<font color="Red">00.文字列の逆順のとこ追記しました。(2020/04/30 18:12) </font>


自然言語処理の勉強と発信も兼ねて、忘備録としてまとめていく。
問題は、乾・岡崎研究室が公開している、[自然言語処理100本ノック](http://www.cl.ecei.tohoku.ac.jp/nlp100/#)。
Python勉強して数ヶ月程度なので、間違っているとこあると思いますが、指摘していただけると幸いです。

実行環境
OS:macOS Mojave
Python: python 3.7.4



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

```Python3:main.py
s = 'stressed'
print(s[::-1])
```

```Python3:Output
desserts
```


~~なんと'stressed'は逆から読むと、'desserts'になるのか。つまり、ストレスが掛かっている状態は裏を返せば、デザートのご褒美みたいなものだよということか。~~

`[start:end:(step)]`で実行できる、文字列操作は非常に便利。
(ちなみに`start`、`end`を省略した場合は全部という意味。)

**・`end`で指定した文字列要素は含まれないので注意。**
例えば、

```Python3:For_example1
s = 'stressed'
print(s[:2])
```

```Python3:Output
st
```

となり`s[2]=r`は含まれない。

**・`step`はその文字から何文字目かを選択する**

```Python3:For_example2
s = 'stressed'
print(s[::2])
```

```Python3:Output
srse
```
sから2文字目(r)、rから2文字目(s(e`s`s))というふうに考えるのが良さそう。
~~**マイナス**が付く場合は、逆から読め！ということができる。~~
![Screen Shot 2019-10-01 at 11.36.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284549/b6ac843d-78bb-6240-e833-2b3c24661223.png)

~~上図のように考えれば良さそう。
`s='0123456789'`という文字列に対して、`432`という出力がほしい場合、
`s[2:5:-1]`でいけそうだが、この操作は定義できない。**マイナス**になると逆から読むため、上の図から~~

<font color="Red">2020/4/30 18:12 追記
上記のような認識で解くことができるが、直感的でなかったため、こちらに修正します。申し訳ありません。</font>

**逆に並べて -> そこから抜き出す**。

つまり、
`s[::-1]` -> `s[::-1][5:8]`とする。

なので、

```Python3:For_example3
s = '0123456789'
print(s[::-1][5:8]) // print(s[4:1:-1]) 同じ 

```

```Python3:Output
432
```
とする。**`end`に関しては`step`に対して`s[end]`の前の文字が含まれるという認識のが良さそう。**

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

```Python3:main.py
s = 'パタトクカシーー'
print(s[::2])
```

```python:Output
パトカー
```

これは`step`を変えれば良い。

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

```Python3:main.py
s = 'パトカー'
t = 'タクシー'

ans = ''
for i, j in zip(s, t):
    ans += i
    ans += j
print(ans)
```

```python:Output
パタトクカシーー
```
使ったのは、`zip(*iterables)`
###`zip(*iterables)`
>それぞれのイテラブルから要素を集めたイテレータを作ります。この関数はタプルのイテレータを返し、その i 番目のタプルは引数シーケンスまたはイテラブルそれぞれの i 番目の要素を含みます。このイテレータは、入力イテラブルの中で最短のものが尽きたときに止まります。単一のイテラブル引数が与えられたときは、1 要素のタプルからなるイテレータを返します。引数がなければ、空のイテレータを返します。[Python 公式](https://docs.python.org/ja/3/library/functions.html?highlight=zip#zip)

つまり`zip()`を使うとイテラブル(リスト、タプル、ディクショナリー、とかのfor文でぶん回すやつ)を一つにまとめて処理できるよーということ

短い方に合わせるとのことなので、

```Python3:For_example1
s = '12345'
t = '1234567'

ans = []
for i, j in zip(s, t):
    ans += (i, j)
print(ans)

```

```Python3:Output
['1', '1', '2', '2', '3', '3', '4', '4', '5', '5']
```

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

```Python3:main.py
s = 'Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.'
words = s.split()
res = []
for w in words:
    res.append(len(w.rstrip(',.')))
print(res)
```

```Python3:Output
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
```
今回使ったのは、`str.split()`、`str.strip()`、`list.append()`

###`str.split(sep=None, maxsplit=-1)`
>文字列を sep をデリミタ文字列として区切った単語のリストを返します。maxsplit が与えられていれば、最大で maxsplit 回分割されます (つまり、リストは最大 maxsplit+1 要素になります)。 maxsplit が与えられないか -1 なら、分割の回数に制限はありません (可能なだけ分割されます)。 [Python 公式](https://docs.python.org/ja/3/library/stdtypes.html#str.split)

引数2つ取れるのか.....

**デリミタ文字**(delimiter)というのは、区切り文字のこと(wiki)で、引数を省略した場合は、None(空白文字)なので、defaultでスペース(' ')、改行(\n)、タブ(\t)などで、分割して**リスト**を返すよーということ。


```Python3:For_example1
s = '''
1 2 3  4
3   8
9 
'''
words = s.split()
print(words)
```

```Python3:Output
['1', '2', '3', '4', '3', '8', '9']
```

```Python3:For_example2
s = '''
1 2 3  4
3   8
9 
'''
words = s.split(maxsplit=1)
print(words)
```

```Python3:Output
['1', '2 3  4\n3   8\n9 \n']
```

基本的には任意の文字に対して`str.split(',')`などを選択することで、リストを返してくれる。

```Python3:For_example2
s = '1,2,3,4,5,6,7,8,9'
words = s.split(',')
print(words)
```

```Python3:Output
['1', '2', '3', '4', '5', '6', '7', '8', '9']
```
###`str.strip([chars])`
> 文字列の先頭および末尾部分を除去したコピーを返します。引数 chars は除去される文字集合を指定する文字列です。 chars が省略されるか None の場合、空白文字が除去されます。 chars 文字列は接頭語でも接尾語でもなく、そこに含まれる文字の組み合わせ全てがはぎ取られます。[Python 公式](https://docs.python.org/ja/3/library/stdtypes.html#str.strip)


```Python3:For_example1
s = '  123  34     '
words = s.strip()
print(words)
```

```Python3:Output
123  34
```

```Python3:For_example2
s = '@@123  34@@@@@@@'
words = s.strip('@')
print(words)
```

```Python3:Output
123  34
```


また,
左のみ、右のみの場合は、それぞれ`str.lstrip([chars])`、`str.rstrip([chars])`を使う。

```Python3:For_example3
s = '  123  34     '
words = s.lstrip()
print(words)
```

```Python3:Output
123  34     #←空白文字あり
```
```Python3:For_example4
s = '  123  34     '
words = s.rstrip()
print(words)
```

```Python3:Output
  123  34
```
また、`str.strip([chars])`は引数を複数取ることができるので、

```Python3:For_example5
s = '@@%@+@123  34+++@@@@'
words = s.strip('@+%')
print(words)
```

```Python3:Output
123  34
```

###`list.append(x)`
> リストの末尾に要素を一つ追加します。a[len(a):] = [x] と等価です。[Python 公式](https://docs.python.org/ja/3/tutorial/datastructures.html?highlight=append)

これはそのまんま。リストの末尾に新しい要素を追加する。


```Python3:For_example1
s = '1 2 3 4 5'
words = s.split()
words[len(words):] = ['9']
print(words)

words.append('10')
print(words)
```

```Python3:Output
['1', '2', '3', '4', '5', '9']
['1', '2', '3', '4', '5', '9', '10']
```
こういう使い方もできると。


##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文字を取り出し，取り出した文字列から単語の位置（先頭から何番目の単語か）への連想配列（辞書型もしくはマップ型）を作成せよ．


10/7修正

```Python3:For_example1
s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
num = [1, 5, 6, 7, 8, 9, 15, 16, 19]

words = s.split()
ans = {}

for i in range(len(words)):
    flag = False
    for j in num:
        # print(i, j)
        if j - 1 == i:
            flag = True
    if flag:
        ans[words[i][0]] = i+1
    else:
        ans[words[i][:2]] = i+1

print(ans)
```

```Python3:Output
{'H': 1, 'He': 2, 'Li': 3, 'Be': 4, 'B': 5, 'C': 6, 'N': 7, 'O': 8, 'F': 9, 'Ne': 10, 'Na': 11, 'Mi': 12, 'Al': 13, 'Si': 14, 'P': 15, 'S': 16, 'Cl': 17, 'Ar': 18, 'K': 19, 'Ca': 20}
```

うおーなんかすごく無駄が多いコードになってしまった。

やってることとしては、
①`str.split()`を使って、単語のリスト(`words`)を得る
②特徴的な1, 5, 6, 7, 8, 9, 15, 16, 19番目をリストとして、単語のインデックスごとに、`num`に含まれるかcheck。`num`に含まれてたら`flag`を立てる。
③`flag`ごとの処理を行う。

なんか煩雑だけど、こんな感じでいいのかな。


#あとがき
初投稿で書くのものすごく時間かかったけど、こういうの書くの好きなんだなと気づいたのでもっと書いていこうと思う。
ただものすごく時間がかかってしまった。~~本当は第1章全部の予定だったんだが.....~~

とりあえず、言語処理100本ノックは今月中にやり終える予定。