Linuxを使えるようになろう #4

# Linux上でPythonプログラムを動かそう

これまでの勉強会でプログラムを動かす際には，Jupyter NotebookやGoogle Colabolatoryなどを使用してきたが，より複雑なプログラムを動かすには，これらでは限界がある．

そこで，今回はLinux上で，Pythonプログラムを動作させる方法を学んでいこう.

# emacsを使いこなそう

これまでもemacsは使用してもらったが，改めて，コマンドのおさらい.
よく使いそうなものをいくつか．


|コマンド|操作内容|
|-|-|
|Ctrl+X Ctrl+S|ファイル変更の保存|
|Ctrl+X Ctrl+C|emacsの終了|
|Ctrl+S|文字列の検索|
|Ctrl+K|カーソル位置から後ろを切り取り|
|Ctrl+Y|切り取ったものを貼り付け|

# Pythonのプログラムを書いてみよう

test.pyというファイルを作成し下記のプログラムを書いてみある．


```test.py
#!/usr/bin/python3
# -*- coding : utf-8 -*-

print("Hello World!")
```

## シバン(shebang)

1行目に記述されているのは，シバンと言われ，記述したプログラムをどのインタープリタで動作させるのかを指定する．
ここでは，python3で実行するので，python3のパスを指定すれば良い．

```.py
#!/usr/bin/python3
```

## 文字コードの指定

2行目には，プログラムコードの文字コードを指定する．
pythonでは，基本的にはUTF-8を使うので，指定しておく，ここを指定していない場合，プログラム中に日本語を使うとエラーが起きてしまう．

```.py
# -*- coding : utf-8 -*-
```

## 実行してみよう

コマンドライン上でpythonプログラムを実行するには2つの方法がある．インタープリタを指定して実行するか．シバンで設定されたインタープリタで実行する．

### インタープリタを指定して実行してみよう

インタープリタを指定して実行するには下記でOK.

```
$ python3 test.py
```

### シバンのインタープリタで実行してみよう

こちらで，実行する際には，実行権限を付与しておく必要がある．
実行権限の不要は下記のように行う．

```
$ chmod 755 test.py
```

実際に実行してみよう．
実行するには，プログラムのパスをコマンドラインに入力すれば良い．絶対パスでも相対パスでもOK.

```
$ ./test.py
```

# 簡単にプログラムを書いてみよう

言語処理100本ノックの準備運動をやってみる
https://nlp100.github.io/ja/ch01.html

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

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

```.py
text = "stressed"
print(text[::-1])
```

出力
```
desserts
```

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

上記のと同じようにして，startとstepを指定すれば良い．
1文字目から2文字ごとにスキップすれば良い．


```.py
text = "パタトクカシーー"
print(text[1::2])
```

出力
```
タクシー
```

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

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


```.py
text1 = "パトカー"
text2 = "タクシー"
print("".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."
print(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]
print(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))
print(gen_Ngram(text, 2, 2, word_ngram=False))
```

出力
```
['I am', 'am an', 'an NLPer']
['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) #和集合
print(ngram1 & ngram2) #積集合
print(ngram1 - ngram2) #差集合
print('se' in ngram1)
print('se' in ngram2)
```
出力
```
['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er']
{'pa', 'ra', 'ap', 'ar'}
{'se', 'ad', 'di', 'is'}
True
False
```

### 07. テンプレートによる文生成Permalink
引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ．さらに，x=12, y=”気温”, z=22.4として，実行結果を確認せよ．

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

```.py
x=12
y="気温"
z=22.4
text = "{}時の{}は{}"
print(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)
print(cipher(cipher_text))
```

出力
```
Hvool Wliow
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 ."
print(" ".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 .
```