タイ語処理OSSに4発プルリクを出した初コントリビューション体験記②; 文章正規化

:::note
前回はこちら：
[タイ語処理OSSに4発プルリクを出した初コントリビューション体験記①; 形態素解析](https://qiita.com/konbraphat51/items/bb5ee37f7274907817a9)
:::

## はじめに
タイ語が読めるので、インターン先で社内唯一のタイ語NLPデータサイエンサーとして登用されている者です。

プログラミングは9年以上やっているのですが、初めてOSSにコントリビューションできて嬉しくなったので、その手順と、そのOSSの管理手法について記録を残します。

## `pythainlp`とは？

タイ語の自然言語処理を行うにあたって恐らく全人類が使うであろう大御所ライブラリ [pythainlp](https://github.com/PyThaiNLP/pythainlp) があります。

これには、
* 形態素解析
* 文章正規化
* ローマ字化
* スペルチェック
* コーパス提供
* ...


など、タイ語の処理に関する森羅万象の機能が搭載されています。

管理は、[backさん](https://github.com/bact)と[wannaphongさん](https://github.com/wannaphong)の2名が積極的に行っております。

## プルリク2; 文章正規化

タイ人はSNSなどでフランクに話す時、感極まると同じ文字を連打します

> สัตตตตตตตต
良いいいいいいいいい

のように。(正しくは`สัต`だけ)

これは、形態素解析のときに邪魔です。消えてもらいたいので、消す関数を作り、プルリクを出しました。

### まずはissue
:::note
このプルリクのお話をしています：
https://github.com/PyThaiNLP/pythainlp/pull/862
:::
自分の計画がライブラリに無用かどうかを確認するために、何がともあれissueで提案・確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/c42d5548-8411-2376-85e6-b631c9eb8b87.png)

管理者からGOサインが出ました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/b1c57827-25c9-a872-4daf-5f642b9e0147.png)

プルリクのコメントにissue番号(`#`の後にある番号)を書き込むと、自動的に下部にあるように「mentioned」が書き込まれるので、取り組んでいることが分かりやすいです。

### 要件定義
issueの通り、実はすでに実装済みのことを移すだけなのですが、一応。

[テストコード](https://github.com/PyThaiNLP/pythainlp/blob/a24407da3a83720df359fa4849f7364d594f163e/tests/test_util.py#L886C9-L892C10)からそのまま転載しますが、
```python
self.assertEqual(
    remove_trailing_repeat_consonants(
        'อืมมมมมมมมมมมมม คุณมีบุคลิกที่เริ่ดดดดด '
        'ฉันจะให้เกรดดีกับคุณณณ\nนี่เป็นความลับบบบบ'
    ),
    'อืมมม คุณมีบุคลิกที่เริ่ด ฉันจะให้เกรดดีกับคุณ\nนี่เป็นความลับ'
)
```
このようなことがしたいです。

すなわち、

>อื<font color="#1e90ff">มมมมมมมมมมมมม</font> คุณมีบุคลิกที่เริ่<font color="#1e90ff">ดดดดด</font> ฉันจะให้เกรดดีกับคุ<font color="#1e90ff">ณณณ</font>
นี่เป็นความลั<font color="#1e90ff">บบบบบ</font>
うーーーーーーーーーん、キミいいねえええええええ。良い成績を上げるよおおおおおお
これは秘密だよおおおおおおお

を

>อื<font color="#1e90ff">มมม</font> คุณมีบุคลิกที่เริ่<font color="#1e90ff">ด</font> ฉันจะให้เกรดดีกับคุ<font color="#1e90ff">ณ</font>
นี่เป็นความลั<font color="#1e90ff">บ</font>
うーん、キミいいね。良い成績を上げるよ。
これは秘密だよ。

にしたいわけです。

まず、傾向として、
* スペース・改行の直前に繰り返し子音が現れる
* 末尾が繰り返すのが正しい単語もある

というところがありますね。
なので
* **スペース・改行・EOSの直前を見ればよく、**
* **しかし繰り返された子音を全て消さばいいわけではない**
    * **→辞書ベースに合致する単語が現れるまで消していく**

ということですね。

### 実装
[124行のずいぶんおデブな関数を最初に提出しちゃいました](https://github.com/konbraphat51/pythainlp/blob/16c3154aa2893236553ac74d836421f82b8768d2/pythainlp/util/normalize.py#L223C1-L223C1)

すると、レビュワーが出てくるまでもなく、Actionからかなり指摘をうけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/9a9f3dae-3bbd-b23a-95ef-b215f965622b.png)

`Linter`からコード規約違反の警告と、`CodeClimate`から「関数デカすぎ」の警告。

#### `__init__.py`
[`__init__.py`にユーザーへ提供したい関数を書き込み](https://github.com/PyThaiNLP/pythainlp/blob/a24407da3a83720df359fa4849f7364d594f163e/pythainlp/util/__init__.py#L97)
これにより、ユーザーは頑張って関数のあるファイルのパスを探さなくとも
```python
from pythainlp.util import remove_trailing_repeat_consonants
```
として`import`できます。

#### テストコード
[テストコードを書きました](https://github.com/PyThaiNLP/pythainlp/blob/a24407da3a83720df359fa4849f7364d594f163e/tests/test_util.py#L869)
お恥ずかしながら、今までテストコードをちゃんと書いたことがないのですが、書いてみると安心感が違いますね。現在は笑顔でテストコード書くようにしています。

#### リファクタリング；コード規約
指摘事項をそのまま直しても、Linterが許可してくれず、そのまま不貞寝しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/653948e6-ac1e-9efa-b3c1-53e34a1918d0.png)

ここで、[CONTRIBUTING.md](https://github.com/PyThaiNLP/pythainlp/blob/dev/CONTRIBUTING.md)を見ると、[`Black`](https://github.com/psf/black)を使うように指示されていました。

すると1コマンドだけでこんな簡単にコード規約関係が解決。めっちゃ便利！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/b1438f48-15b1-d6c0-1c58-c26884012c28.png)

[`Black`](https://github.com/psf/black)すごく良くて、`pip`でインストールして
```
python -m black パス --line-length=79
```
のコマンドを打つだけでそのファイルを`PEP8`通りに仕立て上げてくれます。現在は愛用しております。

#### リファクタリング；関数分解
責務ごとに関数を分解すると、最終的に[256行のファイルに。](https://github.com/konbraphat51/pythainlp/blob/3315cb026fc2e2b8b4518c3ae3a9b04c50738b10/pythainlp/util/remove_trailing_repeat_consonants.py)
`CodeClimate`の警告はなくなりましたが、これでよかったのかなあ。

:::note
CodeClimateについては：
[【GitHub】レビュワーがいなくとも綺麗にコード書く気になるサービス2選と利用方法](https://qiita.com/konbraphat51/items/e82da55e50e0b7a2281f)
:::

#### コードレビュー対応
Action全て対応できたので、コメントで報告。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/6eeba29a-cd9a-fcfa-cfd3-c46cdbd6af3e.png)

関数名の指摘。確かにそうだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/c72dcbbd-9878-cf98-01f2-6d9ba1c92b64.png)

コメントの誤りが複数。ごめんなさい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/39d28ac8-275b-7935-52f8-744f92323286.png)

ライブラリを制作する場合は、`__init__.py`では`import`のみならず`__all__`というのも指定しないといけないらしいです。（じゃないと`pylance`に認識されない）初めて知った。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/eb7ac8f5-0afd-01eb-ab02-de06f189fe26.png)

これらを修正して、`commit`&`push`すると、自動的にプルリクが更新されます。（別件で初めてプルリク出した時にこれを知らずにプルリクを削除して怒られました）

#### merge
そしてマージしていただけました。嬉しい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/632644a5-155e-784d-211a-c36520f86277.png)

## 最後に
まだプルリク2発分ありますが、明日ぐらいに書きます。

ここで総括するまでもなく、`python`のライブラリ管理を実践的に学べたので、すごくいい経験になりましたね。

:::note
いいね頂けると泣きながら喜びます><

この記事を読んでいる方は、これらの記事も読んでいるのかもしれません:
* [【GitHub】レビュワーがいなくとも綺麗にコード書く気になるサービス2選と利用方法](https://qiita.com/konbraphat51/items/e82da55e50e0b7a2281f)
* [【Python】発音で検索できるタイ語辞書アプリを作った](https://qiita.com/konbraphat51/items/ed11678d10cc5aeea9f9)
:::