WordNet × Go で類義語を抽出してみた

# はじめに

最近になって Go 言語を使い始めました。
自分が作りたいアプリで類義語を検索したくて調べたところ、以下の記事を見つけました。
これを Go 言語で書換してみようと思いました。

[Wordnet × Pythonで類義語を抽出する - YoheiM .NET]
(http://www.yoheim.net/blog.php?q=20160201)

# WordNet とは

プリンストン大学が始めたプロジェクトで、シソーラス（語句を意味によって分類・配列した語彙集）を作成しています。
これを元に国立研究開発法人情報通信研究機構（NICT）が、日本語版を作成しています。日本語シソーラスとして無料で自由に使えます。

# WordNet 辞書を Go 言語で利用してみた

* Go 1.10
* WordNet 1.1

## WordNet 辞書を入手する

以下のページから sqlite3 のデータベースファイルをダウンロードします。

[日本語 WordNet]
(http://compling.hss.ntu.edu.sg/wnja/)

ワークスペースに **wnjpn.db** を置きます。

## sqlite3 用パッケージをインストール

Go 言語環境に sqlite3 を利用するパッケージをインストールします。

[mattn/go-sqlite3: sqlite3 driver for go using database/sql]
(https://github.com/mattn/go-sqlite3)

```
go get github.com/mattn/go-sqlite3
```

## WordNet 辞書を利用するプログラム

```main``` パッケージを実装します。

```main.go
package main

import (
	"database/sql"
	"fmt"
	"os"

	_ "github.com/mattn/go-sqlite3"
)

type word struct {
	wordid int
	lang   string
	lemma  string
	pron   string
	pos    int
}

func getWordsFromLemma(db *sql.DB, lemma string) []word {
	rows, _ := db.Query("select * from word where lemma=?", lemma)
	defer rows.Close()
	var words = []word{}
	for rows.Next() {
		var w word
		rows.Scan(&w.wordid, &w.lang, &w.lemma, &w.pron, &w.pos)
		words = append(words, w)
	}
	return words
}

type sense struct {
	synset string
	wordid int
	lang   string
	rank   int
	lexid  int
	freq   string
	src    string
}

func getSensesFromWord(db *sql.DB, word word) []sense {
	rows, _ := db.Query("select * from sense where wordid=?", word.wordid)
	defer rows.Close()
	var senses = []sense{}
	for rows.Next() {
		var s sense
		rows.Scan(&s.synset, &s.wordid, &s.lang, &s.rank, &s.lexid, &s.freq, &s.src)
		senses = append(senses, s)
	}
	return senses
}

type synset struct {
	synset string
	pos    string
	name   string
	src    string
}

func getSynset(db *sql.DB, synsetid string) synset {
	row := db.QueryRow("select * from synset where synset=?", synsetid)
	var s = synset{}
	row.Scan(&s.synset, &s.pos, &s.name, &s.src)
	return s
}

func getWordsFromSense(db *sql.DB, sense sense, lang string) []word {
	rows, _ := db.Query("select word.* from sense, word where synset=? and word.lang=? and sense.wordid = word.wordid", sense.synset, lang)
	defer rows.Close()
	var words = []word{}
	for rows.Next() {
		var w word
		rows.Scan(&w.wordid, &w.lang, &w.lemma, &w.pron, &w.pos)
		words = append(words, w)
	}
	return words
}

func getWordsFromSenses(db *sql.DB, senses []sense, lang string) map[string][]string {
	var synms = make(map[string][]string)
	for _, s := range senses {
		var lemmas []string
		var words = getWordsFromSense(db, s, lang)
		for _, w := range words {
			lemmas = append(lemmas, w.lemma)
		}
		synms[getSynset(db, s.synset).name] = lemmas
	}
	return synms
}

func main() {
	// データベースを利用する準備
	var db *sql.DB
	db, err := sql.Open("sqlite3", "./wnjpn.db")
	if err != nil {
		os.Exit(1)
	}
	defer db.Close()

	// コマンドライン引数を確認
	if len(os.Args) < 2 {
		fmt.Println("検索したい単語を指定して下さい")
		os.Exit(1)
	}
	// コマンドライン引数から検索したい単語を取得
	lemma := os.Args[1]
	// 類義語を検索して表示
	words := getWordsFromLemma(db, lemma)
	for _, w := range words {
		senses := getSensesFromWord(db, w)
		for _, s := range senses {
			fmt.Printf("[%s]\n", getSynset(db, s.synset).name)
			synoms := getWordsFromSense(db, s, "jpn")
			for _, t := range synoms {
				fmt.Printf("  %s\n", t.lemma)
			}
		}
	}
}

```