今回のあらすじ
前回の記事、「Nim言語を使って簡単に文章の類似度を計算してみる」の続きになります。
今回は文章の類似度を計算するGUIアプリケーションに挑戦していきます。
NiGUI
NiGUIは「cross-platform desktop GUI toolkit」ということで、
Windows, Mac, Linuxで動作するGUI用のライブラリです。
https://github.com/trustable-code/NiGui
アプリケーションを作成する
NiGUIのインストール
nimble install nigui
Macの場合
MacでNiGUIを利用しようとすると、
could not load: libgtk-3.0.dylib
と怒られることがあります。
その場合はbrewなどで必要なライブラリを入れてあげます。
brew install libgtk
準備
せっかくなので前回のコードを使いまわしましょう。
Nim言語を使って簡単に文章の類似度を計算してみるのコードを「ngram.nim」、
今回のコードを「nigui_test.nim」として以下のように保存します。
ngram.nimの中身
一応コピペができるように前回のコードをこちらに記載しておきます。
まだ読んでない方はぜひNim言語を使って簡単に文章の類似度を計算してみるを読んでみてくださいね!
import unicode import tables import math proc createNGram*(n: int, text: string): seq[string] = ## ## n-gramデータを作成します ## ## n: n-gramのnに当たる数値 ## text: n-gramに分解(コーパス)したい文字列 # マルチバイト文字列を扱えるようにテキストをルーン化する(Goとかにもある、アレ) let runeText = text.toRunes() # 何文字目まで連結させたかを保持しておく変数(Runeは1文字ずつ扱うため、indexでどこまで扱ったかをカウントしておく) var index = 0 # n-gramで何文字ずつの文字列(コーパス)にするかを決めるためのカウント変数 var cnt = 0 # n-gramでの文字列を作成する際に利用するtmp変数 var tmp: string while true: # cntがn-gramの指定文字数を超えたらそこで切り出す(安全のため<=としている) if n <= cnt: # resultは暗黙変数(nimでは返り値を定義すると自動的にresult変数が生成される) result.add(tmp) tmp = "" cnt = 0 # n-gramの特性上一つ前の文字をもう一度利用するので、n-1をしている index = index - (n - 1) if text.runeLen() <= index: break # 1文字ずつ連結していく tmp = tmp & $runeText[index] cnt = cnt + 1 index = index + 1 proc tf*(corpus: seq[string]): Table[string, int] = ## ## コーパスの中のTFを計算します ## ## corpus: コーパスが格納されたseq配列を指定します for c in corpus: # 連想配列にその単語があれば1加算、なければその連想配列のキーを作成し、1を代入 if result.hasKey(c): result[c] += 1 else: result[c] = 1 proc cosineSimilarity*(text1: string, text2: string, ngramNum: int): float = ## ## 文章の類似度を調べます ## ## text1: 1つ目の文章 ## text2: 2つ目の文章 ## ngramNum: 何gramにテキストを分解するか ## # 文章をそれぞれコーパスに分解します let text1Copus = createNGram(ngramNum, text1) let text2Copus = createNGram(ngramNum, text2) # text2のTF値を求めます let text2Tf = tf(text2Copus) # コサイン類似度の計算に必要な分子分母の変数 var c = 0.0 var m1 = 0.0 var m2 = 0.0 for t1c in text1Copus: # text2のコーパスにtext1のコーパスがあるかないかで類似度を計算することにします # text2のコーパスにtext1のコーパスがあれば1、なければ0を使います var n = 0.0 if text2Tf.hasKey(t1c): n = 1.0 # コサイン類似度に利用する分子分母の数値を計算 c += (1 * n) m1 += 1 * 1 m2 += n * n # コサイン類似度の計算 if m1 == 0 or m2 == 0: return 0 result = c / round(sqrt(m1) * sqrt(m2))
GUIの作成
NiGUIを使ってGUIを作成していきます。
import nigui app.init() # ウインドウの作成 var window = newWindow("テキストの類似度を計算する") # ウインドウのサイズを設定 window.width = 600.scaleToDpi window.height = 265.scaleToDpi # ボタンなどを表示する領域の作成 var container = newContainer() window.add(container) # テキストエリアを作成 var textArea1 = newTextArea() container.add(textArea1) textArea1.x = 0 textArea1.y = 0 textArea1.width = 290 textArea1.height = 200 # テキストエリアを作成 var textArea2 = newTextArea() container.add(textArea2) textArea2.x = 310 textArea2.y = 0 textArea2.width = 290 textArea2.height = 200 # 類似度計算ボタンを作成 var calcButton = newButton("類似度の計算") container.add(calcButton) calcButton.x = 480 calcButton.y = 220 calcButton.width = 100 calcButton.height = 35 window.show() app.run()
上記のコードを実行してみます。
$ nim c -r nigui_test.nim
すると、以下のような画面が生成されます。
これでGUIの基盤ができました。
ボタンを押したら類似度を計算し、表示させる
Nim言語を使って簡単に文章の類似度を計算してみるのコード(ngram.nim)を読み込みます。
import ngram
次にボタン押下時の処理を追加します。
# 類似度計算ボタン押下時の処理 calcButton.onClick = proc(event: ClickEvent) = let textArea1Text = textArea1.text let textArea2Text = textArea2.text # 2つの文章の類似度を計算 let similarity = ngram.cosineSimilarity(textArea1Text, textArea2Text, 2) # メッセージボックスで、計算した類似度を表示 window.alert("2つの文章の類似度は" & $similarity & "です。")
これで準備が完了です。
全体のコードを以下に記載いたします。
import nigui import ngram app.init() # ウインドウの作成 var window = newWindow("テキストの類似度を計算する") # ウインドウのサイズを設定 window.width = 600.scaleToDpi window.height = 265.scaleToDpi # ボタンなどを表示する領域の作成 var container = newContainer() window.add(container) # テキストエリアを作成 var textArea1 = newTextArea() container.add(textArea1) textArea1.x = 0 textArea1.y = 0 textArea1.width = 290 textArea1.height = 200 # テキストエリアを作成 var textArea2 = newTextArea() container.add(textArea2) textArea2.x = 310 textArea2.y = 0 textArea2.width = 290 textArea2.height = 200 # 類似度計算ボタンを作成 var calcButton = newButton("類似度の計算") container.add(calcButton) calcButton.x = 480 calcButton.y = 220 calcButton.width = 100 calcButton.height = 35 # 類似度計算ボタン押下時の処理 calcButton.onClick = proc(event: ClickEvent) = let textArea1Text = textArea1.text let textArea2Text = textArea2.text # 2つの文章の類似度を計算 let similarity = ngram.cosineSimilarity(textArea1Text, textArea2Text, 2) # メッセージボックスで、計算した類似度を表示 window.alert("2つの文章の類似度は" & $similarity & "です。") window.show() app.run()
動作
おわりに
さて、一通りソフトウェアの開発ができました。
Nimはなかなかおもしろい言語ですので、よかったらはまってみてください!
(Excite内でもはまっている人もいます!)
では、また次回!