言語処理100本ノック 2020をやってみる 第一章:準備運動

#目的
下記のウェブサイトの言語処理100本ノックを行う．

https://nlp100.github.io/ja/

#第一章:準備運動
プログラミング言語はPythonを用いる．
jupyter notebookを使用しているので，出力にprint文が必要な場合は適宜追加する．

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

reversed関数を使うことで，文字列を逆順のリスト化を行っている．
リストをjoin関数を用いて結合させ文字列として出力．

```.py
text = "stressed"
print("".join(reversed(text)))
>>>desserts
```

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

Pythonは文字列に位置番号を指定することで，必要な部分のみ抽出できる．
引数は[start : end : step]のように指定する．
start : 初期文字位置
end   : 最終文字位置
step  : スキップする間隔
不要な値は指定しなくて良い．

```.py
text = "パタトクカシーー"
text[1::2]
>>>'タクシー'
```

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

zip関数は複数のリストの要素をまとめることができる．文字列の場合は自動でリスト化した後まとめられる．
sum関数はリストの合計値を取得できる．特殊な使い方として，二重リストをフラット化することが可能．
最後に.join関数でリストを結合し文字列に変換．


```.py
text1 = "パトカー"
text2 = "タクシー"
"".join(sum(zip(text1, text2), ()))
>>>'パタトクカシーー'
```



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

split関数により，スペースで分割しリスト化．
lambda式を使い各単語の長さをリスト化．

```.py
text = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
list(map(lambda x: len(x), text.split()))
>>>[3, 1, 4, 1, 6, 9, 2, 7, 5, 3, 5, 8, 9, 7, 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文字を取り出し，取り出した文字列から単語の位置（先頭から何番目の単語か）への連想配列（辞書型もしくはマップ型）を作成せよ．

split関数により，スペースで分割しリスト化．
ensumerate関数を用いて，リスト化した単語それぞれに順番に番号をふる．
lambda式を使い特定位置の単語の場合は，先頭一文字，それ以外は，先頭二文字を抽出し，位置番号とともにmap化

```.py
text = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
l = [1, 5, 6, 7, 8, 9, 15, 16, 19]
list(map(lambda x: (x[1][:1], x[0]) if x[0] in l else (x[1][:2], x[0]), enumerate(text.split())))
>>>[('Hi', 0), ('H', 1), ('Li', 2), ('Be', 3), ('Bo', 4), ('C', 5), ('N', 6), ('O', 7), ('F', 8), ('N', 9), ('Na', 10), ('Mi', 11), ('Al', 12), ('Si', 13), ('Pe', 14), ('S', 15), ('C', 16), ('Ar', 17), ('Ki', 18), ('C', 19)]
```


###05. n-gram
与えられたシーケンス（文字列やリストなど）からn-gramを作る関数を作成せよ．この関数を用い，”I am an NLPer”という文から単語bi-gram，文字bi-gramを得よ．

gen_Ngramは引数として
text : 入力テキスト
minN : 最小n
maxN : 最大n
word_ngram : ワード単位か文字単位かを指定
を使用し，n-gramを返す．

```.py
def gen_Ngram(text, minN, maxN, word_ngram=True):
    ngram = []
    if word_ngram:
        words = text.split()
    else:
        words = text
    for N in range(minN, maxN+1):
        for i in range(len(words)):
            cw = []
            if i >= N-1:
                for j in reversed(range(N)):
                    cw.append(words[i-j])
            else:
                continue
            if word_ngram:
                ngram.append(" ".join(cw))
            else:
                ngram.append("".join(cw))
    return ngram
text = "I am an NLPer"
print(gen_Ngram(text, 2, 2))
>>>['I am', 'am an', 'an NLPer']
print(gen_Ngram(text, 2, 2, word_ngram=False))
>>>['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er']
```

###06. 集合
“paraparaparadise”と”paragraph”に含まれる文字bi-gramの集合を，それぞれ, XとYとして求め，XとYの和集合，積集合，差集合を求めよ．さらに，’se’というbi-gramがXおよびYに含まれるかどうかを調べよ．

上記のn-gramを返す関数を用いてn-gramを作成する．
set関数でリストを集合化．
和集合，積集合，差集合を求める．
最後にそれぞれの集合に'se'が含まれるかを判定している．

```.py
text1 = "paraparaparadise"
text2 = "paragraph"
ngram1 = set(gen_Ngram(text1, 2, 2, word_ngram=False))
ngram2 = set(gen_Ngram(text2, 2, 2, word_ngram=False))
print(ngram1 | ngram2) #和集合
>>>{'ra', 'ap', 'ar', 'ad', 'gr', 'is', 'ag', 'pa', 'di', 'se', 'ph'}
print(ngram1 & ngram2) #積集合
>>>{'pa', 'ra', 'ap', 'ar'}
print(ngram1 - ngram2) #差集合
>>>{'se', 'ad', 'di', 'is'}
print('se' in ngram1)
>>>True
print('se' in ngram2)
>>>False
```
###07. テンプレートによる文生成Permalink
引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ．さらに，x=12, y=”気温”, z=22.4として，実行結果を確認せよ．

format関数を使い変数を代入し表示する．

```.py
x=12
y="気温"
z=22.4
text = "{}時の{}は{}"
text.format(x, y, z)
>>>'12時の気温は22.4'
```

###08. 暗号文
与えられた文字列の各文字を，以下の仕様で変換する関数cipherを実装せよ．

    英小文字ならば(219 - 文字コード)の文字に置換
    その他の文字はそのまま出力

この関数を用い，英語のメッセージを暗号化・復号化せよ．

文字列を1文字ずつリスト化．
islower関数により，文字が小文字か大文字か判定．
ord関数は文字をUnicodeに変換する．
219からUnicodeの値を引いて，chr関数により文字に変換．
全く同じ関数で複合化可能．

```.py
def cipher(text):
    return "".join(list(map(lambda x: chr(219 - ord(x)) if x.islower() else x, list(text))))
text = "Hello World"
cipher_text = cipher(text)
print(cipher_text)
>>>Hvool Wliow
print(cipher(cipher_text))
>>>Hello World
```


###09. Typoglycemia
スペースで区切られた単語列に対して，各単語の先頭と末尾の文字は残し，それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ．ただし，長さが４以下の単語は並び替えないこととする．適当な英語の文（例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."）を与え，その実行結果を確認せよ．

文字列をスペースで分割しリスト化．
4文字以下の場合はそのまま出力する．
それ以外の場合は，先頭の文字と最後の文字はそのままで間の文字をランダムに入れ替える．
ランダムに入れ替えるのにramdam.sample関数を使用している．

```.py
import random
text = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
" ".join(list(map(lambda x: x if len(x) <= 4 else x[0] + "".join(random.sample(list(x)[1:-1], len(x[1:-1]))) + x[-1], text.split())))
>>>"I c'nluodt bliveee that I colud atlcluay urednastnd what I was raeindg : the pnnaemhoel pewor of the human mind ."
```