日本語的文章中国語文字列変換プログラム書了

概要

日本語の文章から平仮名を抜いて漢字だけをつなぎ合わせた、一見すると中国語のように見える文字列を偽中国語というらしい.いわゆるハナモゲラである.

すでに作ってらっしゃる方がいたのだけれど,形態素解析を WebAPI で行ってるとのことで,シェル芸botでは動かせないという問題があって,似たようなものをでっち上げました.

こちらを参考にさせていただきました.

qiita.com

というか,これのサンプルぐらいしか見なかったのでいろいろ微妙かもしれない. 一応,否定っぽいのがあったら という文字を突っ込むように改変してある.

ライセンスは mocobeta さんのおすすめで WTFPL だ.煮るなり焼くなり好きにしてください.PR もお待ちしています.

Change license to WTFPL · Issue #1 · ikawaha/nise · GitHub

こんな感じ

基本的にはひらがな落としてくだけなので,漢字が多い文章の方がそれっぽくなります.

github.com

$ nise 今日は学校に行かない
今日学校行不

$ nise 台風が近づいています
台風近了

$ nise 私はお酒が飲みたい
我酒飲希望

Happy hacking!

メモ:goa v3 のテストを回す

概要

goa v3 のテスト方法.

かなり不確かなメモ.

やり方

goa のレポジトリに Makefile が入ってるので,これでテストを起動する.テストは3種類用意されている.

  • make test
  • make test-examples
  • make test-plugins

一通りテストしたいときは make travis としておけば問題なさそう.

ハマりどころ

  • 依存ライブラリがあって,$GOPATH 以下に go get されるが,-u オプションがついてないので,古いのがあると更新されない.
  • github.com/goadesign/examples$GOPATH/src/goa.design/examples に,github.com/goadesign/plugins$GOPATH/src/goa.design/plugins にコピーして,v3 ブランチを利用しようとするが,ここがすでにある場合にはコピーされないので,齟齬がでるときがある
  • これは過渡的だと思うが,作業ディレクトリは,$GOPATH/src/goa.desing/goa が想定されている.(make depend がこのディレクトリでの作業を想定している)

その他,気になったところ

  • Makefile 内で PROTOC_VERSION が指定されていて,指定されたバージョンがダウンロードされるようになっているが,これが古い可能性がある.

/以上

メモ: goa v3 をいじったりしてデザインに反映させてみる

概要

goa v3 でコードを変更してみたくなったときの goa gen の確認方法.

方法

デザインの方の go.mod で手元の変更したコードを参照するように replace を指定する.

例:

module calc

go 1.12

replace goa.design/goa/v3 => ./../../go/src/github.com/goadesign/goa

require (
    cloud.google.com/go v0.39.0 // indirect
    github.com/PuerkitoBio/purell v1.1.1 // indirect

goa gen のときにてもとの goa のコードを参照してコード生成するので,goa 自体はビルドし直さなくて大丈夫.

作業メモ:kagomeのシステム辞書を更新する

概要

辞書項目追加したので作業記録の自分用メモ.

作業

必要なもの

  • go-bindata コマンド
    • 自分のレポジトリにある改造したやつ

作業ディレクト

kagome/cmd/_dictool 以下で作業する

キーワードを追加する

  • IPADIC
    • mecab-ipadic-2.7.0-20070801 を用意する (github.com/taku910/mecab にあるものでもいい)
    • 差分がとれるように編集するファイルをコピーする
    • EUC-JP で項目を追加
    • diff -up <original> <updated> で差分をとって patch 以下に記録しておく
    • ファイルを更新
  • UniDIC
    • unidic-mecab-2.1.2_src を用意する
    • 差分がとれるように編集するファイルをコピーする
    • utf8 で項目を追加
    • diff -up <original> <updated> で差分をとって patch 以下に記録しておく
    • ファイルを更新

辞書ビルド

IPADIC

$ mkdir ./dic
$ mkdir ./dic/ipa
$ go run main.go  ipa -mecab ./mecab-ipadic-2.7.0-20070801 -output ./dic/ipa -z false
$ go run main.go ipa -mecab ./mecab-ipadic-2.7.0-20070801

UniDIC

$ mkdir ./dic/uni
$ go run main.go  uni -mecab ./unidic-mecab-2.1.2_src -output ./dic/uni -z=false
$ go run main.go uni -mecab ./unidic-mecab-2.1.2_src

ipa.dic と uni.dic を kagome/_sample にコピーして更新

go-bindata

go-bindata -o bindata.go -nomemcopy -separate -pkg=data dic/...

直下に gindata*.go というファイルが生成される.これを kagome/internal/dic/data 以下にコピーする

17:35 $ ls -lah ../../internal/dic/data/
total 432920
drwxr-xr-x@ 24 ikawaha  staff   768B  4 16 16:31 ./
drwxr-xr-x@ 23 ikawaha  staff   736B  2 16 13:09 ../
-rw-r--r--   1 ikawaha  staff   7.7K  4 16 16:31 bindata.go
-rw-r--r--   1 ikawaha  staff   5.9M  4 16 16:31 bindata00.go
-rw-r--r--   1 ikawaha  staff   7.7M  4 16 16:31 bindata01.go
-rw-r--r--   1 ikawaha  staff    20M  4 16 16:31 bindata02.go
-rw-r--r--   1 ikawaha  staff   9.4M  4 16 16:31 bindata03.go
-rw-r--r--   1 ikawaha  staff   7.5M  4 16 16:31 bindata04.go
-rw-r--r--   1 ikawaha  staff   3.0M  4 16 16:31 bindata05.go
-rw-r--r--   1 ikawaha  staff   3.9M  4 16 16:31 bindata06.go
-rw-r--r--   1 ikawaha  staff   4.3M  4 16 16:31 bindata07.go
-rw-r--r--   1 ikawaha  staff   3.6M  4 16 16:31 bindata08.go
-rw-r--r--   1 ikawaha  staff   3.5M  4 16 16:31 bindata09.go
-rw-r--r--   1 ikawaha  staff   3.7M  4 16 16:31 bindata10.go
-rw-r--r--   1 ikawaha  staff   8.6M  4 16 16:31 bindata11.go
-rw-r--r--   1 ikawaha  staff    21M  4 16 16:31 bindata12.go
-rw-r--r--   1 ikawaha  staff    18M  4 16 16:31 bindata13.go
-rw-r--r--   1 ikawaha  staff    18M  4 16 16:31 bindata14.go
-rw-r--r--   1 ikawaha  staff    16M  4 16 16:31 bindata15.go
-rw-r--r--   1 ikawaha  staff    10M  4 16 16:31 bindata16.go
-rw-r--r--   1 ikawaha  staff    12M  4 16 16:31 bindata17.go
-rw-r--r--   1 ikawaha  staff    16M  4 16 16:31 bindata18.go
-rw-r--r--   1 ikawaha  staff    14M  4 16 16:31 bindata19.go
-rw-r--r--   1 ikawaha  staff   4.2M  4 16 16:31 bindata20.go

念のため go fmt する

$ go fmt ../../internal/dic/data/

テスト

項目を追加/削除していれば項目の全体数が合わなくなるのでそのテストが失敗するはず.

$ go test ./...
?       github.com/ikawaha/kagome/cmd/kagome    [no test files]
?       github.com/ikawaha/kagome/cmd/kagome/lattice    [no test files]
?       github.com/ikawaha/kagome/cmd/kagome/server [no test files]
?       github.com/ikawaha/kagome/cmd/kagome/tokenize   [no test files]
ok      github.com/ikawaha/kagome/internal/da   (cached)
--- FAIL: TestSystemDic (1.58s)
    sysdic_test.go:30: got 392127, expected 392126
--- FAIL: TestSystemDicSimple (1.55s)
    sysdic_test.go:41: got 392127, expected 392126
--- FAIL: TestSystemDicIPAMorphs01 (1.42s)
    sysdic_test.go:65: got 392127, expected 392126
--- FAIL: TestSystemDicIPASimpleMorphs01 (1.32s)
    sysdic_test.go:75: got 392127, expected 392126
--- FAIL: TestSystemDicUniMorphs01 (8.42s)
    sysdic_test.go:85: got 756466, expected 756463
--- FAIL: TestSystemDicUniSimpleMorphs01 (7.43s)
    sysdic_test.go:95: got 756466, expected 756463
--- FAIL: TestSystemDicIPAContents01 (1.25s)
    sysdic_test.go:105: got 392127, expected 392126
--- FAIL: TestSystemDicUniContents01 (7.75s)
    sysdic_test.go:125: got 756466, expected 756463
FAIL
FAIL    github.com/ikawaha/kagome/internal/dic  113.520s
?       github.com/ikawaha/kagome/internal/dic/data [no test files]
ok      github.com/ikawaha/kagome/internal/lattice  17.673s
ok      github.com/ikawaha/kagome/splitter  (cached)
--- FAIL: TestAnalyze02 (0.00s)
    tokenizer_test.go:68: got 関西国際空港(0, 6)KNOWN[372978], expected 関西国際空港(0, 6)KNOWN[372977]
FAIL
FAIL    github.com/ikawaha/kagome/tokenizer 20.071s

テストを修正する.

  • 項目数が増えることによる修正
    • 全体のエントリ数
    • 単語IDのずれ

/以上

slackのメッセージをひたすら読み上げさせるコマンドを作った

概要

作業に没頭してると slack のメッセージを取りこぼしてしまうことが多いんですが,これを読み上げてくれば便利だなと雑に作ってみました.

ゲーム実況なんかで,来たメッセージを bot に読み上げさせて,プレイしながら回答してやつのイメージです.

github.com

必要環境

mac というか say コマンドが必要です.

インストール

go get github.com/ikawaha/slacksay/...

使い方

slack トークンを用意して,手元の計算機で下記を実行すれば ok です. トークンの取得はとりあえずレガシートークhttps://api.slack.com/custom-integrations/legacy-tokens を取得するか,過剰な権限が気になるなら https://api.slack.com/apps この辺で取得してください.

slacksay -t <slack_token> [-d (<json data>|@<file_name>|@-)]
  -d string
        json data. If you start the data with the letter @, the rest should be a file name to read the data from, or -  if you  want to read the data from stdin.
  -t string
        slack token

ex.

slacksay -t xoxp-your-token -d '{"bot_message": "true"}'

仕組み

slack に流れてきたメッセージを say コマンドに渡すだけです.

だだし,それだけだとカエルの合唱みたいになってしまうので,slack のメッセージを一端 queue にいれて,整列させてからコマンドに渡します. いろいろ試したんですが,喋らせるのは1つだけにしておくのが無難だというところに落ち着きました.

goroutine 使ったコード書くのは面白いですね!

あと,say コマンドが必ずしも正しく読みを当ててくれるわけではないので,ユーザー名とか読み替えを指定できるようにしてあります.

チャンネルやユーザー,キーワードでのフィルターも用意してみましたが使ってないのでこの機能が必要なのかよく分かっていません. ぼくの環境では,bot のメッセージ(slack では SubType が bot_messageとなるやつ)は除外しないとうるさくてしょうがなっかったので, これを無視するオプションを用意してあります.

say コマンドの代わりに他のコマンドを指定するオプションを用意してありますが,これも不要な気がしています. もしかしたら windows でも似たようなコマンドないかな・・・みたいな期待はあるのですが,実機がないので・・・.

未解決

say コマンドはオプション指定すると声色を変更できるのですが,exec.Command() で指定するとうまく実行できませんでした. なぜだろうか・・・.

Happy hacking!

goa でレスポンスを XML で返す

概要

goa のレスポンスはデフォルトでは json になっているが,これを XML で返したい.

ちなみに,入力の方は json, XML, gob (!!!) がデフォルトで受け取れるようになっている.

例で使ったコードはここにあるやつです :

https://github.com/ikawaha/cellar/tree/xml

方法

cellar の example をちょっと修正して動作を試してみます.

1. デザインで Produces を指定する

package design

import (
    . "github.com/goadesign/goa/design"
    . "github.com/goadesign/goa/design/apidsl"
)

var _ = API("cellar", func() {
    Title("The virtual wine cellar")
    Description("A simple goa service")
    Scheme("http")
    Host("localhost:8080")

    Produces("application/xml") // ★ これを追加
})

var _ = Resource("bottle", func() {
    BasePath("/bottles")
    DefaultMedia(BottleMedia)

    Action("show", func() {
        Description("Get bottle by id")
        Routing(GET("/:bottleID"))
        Params(func() {
            Param("bottleID", Integer, "Bottle ID")
        })
        Response(OK)
        Response(NotFound)
    })
})

var BottleMedia = MediaType("application/vnd.goa.example.bottle+xml", func() { // ★ レスポンスは XML なので適当に変更する
    Description("A bottle of wine")
    Attributes(func() {
        Attribute("id", Integer, "Unique bottle ID")
        Attribute("href", String, "API href for making requests on the bottle")
        Attribute("name", String, "Name of wine")
        Required("id", "href", "name")
    })
    View("default", func() {
        Attribute("id")
        Attribute("href")
        Attribute("name")
    })
})

2. goagen

goagen します.

3. 起動してアクセスしてみる

コントローラーは何も書いてないので,適当に返ってくるのを見るだけ(^^ゞ

$ curl -i localhost:8080/bottles/0
HTTP/1.1 200 OK
Content-Type: application/vnd.goa.example.bottle+xml
Date: Thu, 04 Apr 2019 09:08:28 GMT
Content-Length: 73

<GoaExampleBottle><href></href><id>0</id><name></name></GoaExampleBottle>

レスポンスボディは XML 形式になってます. Content-Type も MediaType で指定したものになっています.

Happy hacking!

2018年の買い物振り返り

はじめに

他の人のレビューですごくいい買い物できたので,自分の今年の買い物も振り返ってみました. もっといいのあるよ!とか教えてもらえればうれしいです.

よかった

体重計

他のブログで紹介されてたのをみて買いました.両親が毎日体重を量って手帳に記録してたのがすごく楽になっていい買い物でした.スマホのアプリで体脂肪とかもグラフ化されます.精度がどうなのかはよく分からないけど,目安だと思って細かいことを気にしないならいいと思う(両親にはそう言い聞かせている).

WiFi

いわゆるメッシュWiFi.一軒家で無線LANルーターを複数台置いていたのを置き換えました.ちょっと高い気もするけど,環境は劇的によくなりました(スプラトゥーンが落ちなくなった.我が家比).セキュリティとか気になるなら Google のやつの方がいいのかもしれない.ちょっと高いけど.

スライサー

こどもの朝ご飯にプログラマがつくる簡単なものと云えばハッシュ.ジャガイモを千切りにして炒めるやつがぼくの定番おかずなのだが,このスライサーはかなり細く切れてよかった.不満点は指ガードが小さくてちょっと頼りなく,なくなりそうなこと(実際なくなった).結局他のスライサーについてた指ガードをつかってる.なければ100均で買ってくるとよさそう.

冷蔵庫

今年一番の大物.真夏に壊れて急遽買いました.壊れた前の冷蔵庫は10年前のもので,470Lくらいだったやつですが,これは 550Lで大きさは同じでした. よく冷やせて,野菜のもちもよくなった気がします.慌てて買ったけどこれはよい買い物でした.

まぁまぁ

ディスプレイ

スプラトゥーンやりたくて買った.値段相応だがゲームする分には満足してる.仕事するにはやや不満がある.

スピーカー

スプラトゥーン用に(ry.特に不満はない.

コーヒーメーカー

使ってたコーヒーメーカーが壊れてしまったので買った.コーヒーミルは手動のがあったので全自動でないやつ.コーヒーが冷めないのがいい.手入れも簡単. だが,なんとなく前の機械の方が上手に淹れられた気がするんだよな・・・.何回か使わないと機械っぽいにおいが取れなかったので気になる人は最初はお湯を何回か通した方がいいかも.

ちょっとしたもの

HDMI ケーブル,こんなに細いのあるならだいぶすっきりするじゃん!ということを知った.

スプラトゥーンやってるとプロコン壊れやすいと聞いたので,プロコンの可動部に塗ってる.これのおかげか今のところ故障はない.

クラッシュアイスが作れる器.使い方が悪いのかこれは微妙だった.口の細い水筒に氷いれたいときにはいいかもしれない.

ゲームとか

宝石の煌き

2〜4人で遊べます.あんまりボドゲ好きじゃない妻もこれは参加してくれる.

宝石の煌き 日本語版

宝石の煌き 日本語版

カヴェルナ

これは1〜2人用.コマが多くて割と複雑だけど,ルールをのみ込めば子供(8歳)でもできる程度.プレイ時間はちょっと長め.ある程度の気合いが要る.コンボが決まったときが面白い.

カヴェルナ:洞窟対決 日本語版

カヴェルナ:洞窟対決 日本語版

パッチワーク

パッチ(端布)を順番に取り合って自分のマス目を埋めてくゲーム.ルールはわかりやすく,ちょっとした駆け引きもあるので子供ものってくれる.コマを並べるのにちょっと広めの机(場所)が必要.終わったときにお互いの成果物を見合ったりして「なかなかだね」とかいうのが面白い.2人用.

パッチワーク 日本語版

パッチワーク 日本語版

context.WithTimeout()を使うとタイムアウトが想像以上にきれいに書けた

概要

slack の Real Time Messaging を使って bot を書いてたんですが,しばらくすると websocket が切断されてて bot が落ちてしまってました.今回はこれにタイムアウト処理を追加してリカバリできるようにした防備録です.

問題のコード

func (c *Client) GetMessage() (Message, error) {
    var msg Message
    if err := websocket.JSON.Receive(c.socket, &msg); err != nil { // ← 切断されちゃうとこいつが沈黙する
        return msg, err
    }
    return msg, nil
}

タイムアウト処理を入れる

context.Context にはタイムアウト処理をうまくやってくれる仕組みがあるのでこれを使います.

func (c *Client) GetMessage() (Message, error) {
    ctx, cancel := context.WithTimeout(context.Background(), Timeout)  // ← タイムアウト付きの context を設定
    defer cancel()
    ch := make(chan error, 1)

    var msg Message
    go func() {
        ch <- websocket.JSON.Receive(c.socket, &msg)
    }()

    select {
    case err := <-ch: // websocket からメッセージがとれればここへ来る.関数を抜けるときに cancel() が defer で呼ばれて context が解放される
        return msg, err
    case <-ctx.Done(): // ← websocket が沈黙してても,時間が来たら Done() が close される
        return msg, fmt.Errorf("connection lost timeout")
    }
    return msg, nil
}

これで時間が来たら timeout でエラーが返るようになります.エラーが返ってきたら websocket をつなぎ直せば解決です.

が,これだと websocket が生きててもタイムアウトして再接続しちゃいます.タイムアウトする前に websocket を ping で叩いてみて,反応があったら単に関数を抜けるようにすれば無駄な再接続を防げそうです.

func (c Client) GetMessage() (Message, error) {
    ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
    defer cancel()
    ch := make(chan error, 1)

    go func(ctx context.Context, waiting time.Duration) {
        ctx, cancel := context.WithCancel(ctx)  // ← 親の context から新しい context を作ります
        defer cancel()
        select {
        case <-ctx.Done(): // ← 親の context が close したらここにメッセージが来て何もしないで抜ける
        case <-time.After(waiting): // timeout するちょっと前に ping を打つ
            websocket.JSON.Send(c.socket, &Message{Type: EventTypePing, Time: time.Now().Unix()})
        }
    }(ctx, c.timeout-time.Second)

    var msg Message
    go func() {
        ch <- websocket.JSON.Receive(c.socket, &msg)
    }()

    select {
    case err := <-ch: // ← メッセージが無くても websocket が生きてればタイムアウト前には pong が返ってくるはず
        return msg, err
    case <-ctx.Done():
        return msg, fmt.Errorf("connection lost timeout")
    }
    return msg, nil
}

とりあえずこれで安定して動いているようです.context.Context を使うと複雑な処理になりそうなところが想像以上に見通しのよいコードがかけて幸せです.

( '-`).oO( うまいことできた!と思ってるけど,もっとうまいことやれるような気もしてて,なにか方法があれば教えて欲しいです

f:id:ikawaha:20181018223422p:plain

Happy hacking!

Travis-CI で go の特定バージョンでだけテストカバレジを取りたい

概要

go の 1.11 がリリースされました 🙌.それに伴って,テストカバレッジの指定方法が変更されて,いままでパッケージひとつごとにしかテストカバレッジ取れなかったのが,パッケージをまとめて指定出来るようになりました.

いままでのテストカバレッジ,地味にめんどくさくて,今までは

#!/bin/bash

COV_FILE=coverage.txt
COV_TMP_FILE=coverage_tmp.cov
ERROR=""

echo "mode: count" > $COV_FILE

for pkg in `go list ./...`
do
    touch $COV_TMP_FILE
    go test -covermode=count -coverprofile=$COV_TMP_FILE $pkg || ERROR="Error testing $pkg"
    tail -n +2 $COV_TMP_FILE >> $COV_FILE || (echo "Unable to append coverage for $pkg" && exit 1)
done

rm $COV_TMP_FILE

if [ ! -z "$ERROR" ]
then
    echo "Encountered error, last error was: $ERROR"
    exit 1
fi

GOPATH=$HOME/gopath
GOVERALLS=$(echo $GOPATH | tr ':' '\n' | head -n 1)/bin/goveralls
$GOVERALLS -coverprofile=$COV_FILE -service=travis-ci

こんな感じのスクリプトを置いておいて,これを travis.yaml から呼び出してたんですが,1.11 では,

go test -covermode=count -coverprofile=$COV_TMP_FILE ./...

のようにまとめて指定出来ます.

がしかし,travis-ci では go のいくつかのバージョンをまとめて指定出来るので,前のバージョンもテストしてカバレッジを取るようにしておくと,go のバージョンを見て,どっちの方式でやるか,あるいは指定バージョンでのみカバレッジ取る,みたいな設定が必要になってきます.

やりたいことは

です.至極単純なことなんですが,設定方法をかなり試行錯誤したので,メモ代わりに置いておきます.

Travis-CI での go のバージョン判別法

変数 $TRAVIS_GO_VERSION に納められているのでこれで判別出来ます. travis-ci では 1.11.x のように末尾をワイルドカードに出来るのですが,このとき,$TRAVIS_GO_VERSION に入っているのは 1.11.x です.実際のバージョンが入っている訳ではないので気をつけてください.

どうやるか

結論から言うとこういう感じです.テストが終わったら,after_success でテストカバレッジを取って coveralls に送信してます.

language: go

go:
  - "1.9.x"
  - "1.10.x"
  - "1.11.x"
  - tip
sudo: false
cache: bundler

git:
  depth: 1

before_install:
  - pip install --user codecov
  - go get github.com/axw/gocov/gocov
  - go get github.com/mattn/goveralls
  - go get golang.org/x/tools/cmd/cover

install:

script:
  - go test ./...
  - cd tokenizer; go test -benchmem -bench .; cd ..
  - cd internal/dic; go test -benchmem -bench .; cd ../..

after_success:
  - |
    if [[ $TRAVIS_GO_VERSION = 1.11.x ]]
    then
      go test -covermode=count -coverprofile=coverage.txt ./...
      $GOPATH/bin/goveralls -coverprofile=coverage.txt -service=travis-ci
    else
      echo skip coverage test, $TRAVIS_GO_VERSION
    fi

うまくいかなかった方法

travis-ci で用意されてる condition とか on とか if を利用するのはうまくいかなかったです.こう云うのでうまくやれた方がスッキリしそうなんですが,もしもっといい書き方あるよ!というのをご存じの方がいらっしゃいましたらお教えください.

Happy hacking!

kagomeが go get できない問題

概要

kagome が go get できない!ということがある模様.

たしかに kagome は辞書をソースコードに変換して保持してるので,重いのは重いのですが,いつまでたっても終わらない・・・ということはないはずと思います. 手元でやってみたら2分〜3分,会社の回線で5分くらいでした.回線が細いと厳しいかもしれません. といっても kagome のレポジトリは 400MB 程度.今時の携帯アプリでも大きめのはこれくらいある時代ですし・・・

原因はレポジトリのサイズの他にもあるかもしれません.もし他にもいらっしゃったら教えてください.

申し訳なさ

go は go get でサクッと使えるように便利にできています.なのに kagome がこのユーザー体験を破壊していると思うと申し訳ない限り. kagome はかなり特殊な重たいライブラリなので,kagome の事を嫌いになっても go を嫌いにならないでください!みたいなお気持でいっぱいです.

回避策

1. go get コマンドの最後の ... は意味があります

$ go get -v github.com/ikawaha/kagome/...

基本はこれでいけるはずです.最後の ... がないと失敗するので注意してください.(でも失敗と分かるので落ちてこないというのは多分これではない

2. 直接 git clone する

$GOPATH/src 以下に github.com/ikawaha/kagome として git clone してみてください.このとき depth を 1 にしてください. (go get はデフォルトで depth 1 で clone してくれます)

$GOPATH設定してない場合は ~/go/src 以下に読みかえてください.

$ git clone --depth 1 https://github.com/ikawaha/kagome.git $GOPATH/src/github.com/ikawaha/kagome
Cloning into '/Users/ikawaha/go/src/github.com/ikawaha/kagome'...
remote: Counting objects: 116, done.
remote: Compressing objects: 100% (103/103), done.
remote: Total 116 (delta 21), reused 60 (delta 6), pack-reused 0
Receiving objects: 100% (116/116), 127.47 MiB | 1.61 MiB/s, done.
Resolving deltas: 100% (21/21), done.
Checking out files: 100% (93/93), done.

3. 軽量版 kagome を使う

kagome には IPADic と UniDic の2種類の辞書が入っています.が,ぶっちゃけ普通の人は UniDic は使わないでしょう. 利用方法が同じで IPADic だけを同梱した kagome.ipadic がありますのでこちらを利用してみてください. こちらは GAE でも動作するように調整してありますので,GAE で使いたい場合にもこちらをご利用ください.

$ go get github.com/ikawaha/kagome.ipadic/...

何かいい方法とかあれば教えて欲しいです・・・.

追記

前より遅くなったんじゃないかとの感想にコメントいただきました.なるほどなー

でも全く落ちてこないってのはやはり変.

追記2

github から直接落としても同じだけ時間がかかるという報告いただきました.なので落とせないときは方法の2を試しても,ダメな可能性がありそう. でもまぁ,そのときは github の転送量のせいだと問題が分離できるのでよしとする(ぉ.

追記3

解決したようです.ヤター

会社引っ越し前の新宿ランチ棚卸し

3年ほど,新宿高島屋前のオフィスで働いてきましたが, 今年の3月くらいに会社が引っ越すことになったので,新宿三丁目のランチでお世話になったお店を棚卸し.

ここも行っておいた方がいいよ!ってところがあったら是非教えてください.

( '-`).oO( とか言って次も新宿だったりするかもだけど

思いついたら追加してく.


tabelog.com

アイリッシュパブ.夜は行ったことないんですが,ランチでよくお世話になりました. ランチは950円で,週替わり3種類とカレーから選ぶ.コーヒー付く.


tabelog.com

夜は焼き鳥屋さん.新宿御苑の新宿側の入り口のところ. ランチは唐揚げかチキン南蛮を週替わりでやっていて,親子丼はレギュラーメニュー.970円.


tabelog.com

ベトナム料理のお店で,ランチはフォーのセットがおすすめ.練乳入りでとっても甘いベトナムコーヒーが付いてくるのも楽しみ.


tabelog.com

最近出来たシュラスコのお店.ランチは,一串にチキン,ソーセージ,牛肉2種x2とズッキーニ,ナスをひとかけずつ焼いたものを出してくれる. ローストビーフ丼もおいしい.どちらも1000円.時間があれば食べ放題のコースもあります.


tabelog.com

カレー2種類とナン,ドリンクが付いて950円ぐらいだった気がする.お腹いっぱいで午後は破滅するがカレー欲は満たされる.


tabelog.com

羊っぽいものが食べられる.夜も来たことあるけど,この店安いよね!


tabelog.com

パスタのお店.うまいです.定番のミートソースにするか,ピリ辛のアラビアータにするか,カルボナーラにするか迷う.麺の量をが200gの大盛りまで同じ金額で選べるので200gしか食べたことがない.+100円すれば 250g もいける.むしろ増量して食べようかな,と思うくらいおいしい.


tabelog.com

北海道スープカレー東京ドミニカ.固有表現抽出泣かせの店名.


tabelog.com

生醤油うどん,すだち,ちくわ天トッピングが大体いつものメニュー.大盛りにしてもお値段そのままだった気がする. おいしい.ただ,出汁の感じは関西風で讃岐うどんではない.とおもう.


tabelog.com

ちょっと遠いけどたまに行くイタリアン.お持ち帰りできるドリンクがサービスされてる.


tabelog.com

夜はオイスターバーで,ランチにも牡蠣入りのカレーとか食べられる. でも過去にノロのトラウマがあるので牡蠣は食べたことない. ランチは1000円くらいから.


tabelog.com

お肉が食べたいときに行くとちょっといい感じの肉が食べられる.


tabelog.com

ピザ,ハーフアンドハーフで頼んで,エスプレッソを飲む.しあわせ.

番外

tabelog.com

今はランチやってないけど,ときどき飲みに行くのに使ってた.やさい中心でおじさんの胃袋にも優しい.


tabelog.com

有名なカレー屋さん.らしい.おいしかったと思うけど,記憶が定かでない.何回かしか行ってなかった.


tabelog.com

牛カツのお店.出来た頃に何回か行ったが,いつも行列してるので通り過ぎるとき眺めるくらいになってしまった.


tabelog.com

知らないとたどり着けない感じの奥まった感じの場所にあるお店.なんとなく健康そうなものが食べられる. 夜もおすすめ.


tabelog.com

いつも混んでる.エビつけ麺のお店.


tabelog.com

電源が充実しているカフェ.


tabelog.com

ひとりの時に時々行くラーメン屋.15時にお店にいるとサービスでチャーシューの切れ端がもらえる.これマメな.


tabelog.com

もっと近くにあったら足繁く通うのにな.ちゃんとした(?)中華が食べられる.


tabelog.com

ちょっと変わった豚汁のお店.1回しか行ってない.もっと行きたかった.

形態素解析器 Sudachi の辞書が手に入ったので Go で遊んでみた

はじめに

Sudachi はワークスアプリケーションズが絶賛開発中の形態素解析器で,Java で書かれています.

今一番新しい注目すべき形態素解析器で,次のような機能が特徴としてあげられています.

  • 複数の分割単位の併用
    • 必要に応じて切り替え
    • 形態素解析と固有表現抽出の融合
  • 多数の収録語彙
    • UniDic と NEologd をベースに調整
  • 機能のプラグイン
    • 文字正規化や未知語処理に機能追加が可能
  • 同義語辞書との連携 (後日公開予定)

github.com

開発のステータスは公式には発表されていない(?)ようですが,開発中で本リリースはこれからかな・・・と思います.(実際のところ,スケジュールなどはどうなんでしょうか?)

最近,Sudachi の辞書がレポジトリからダウンロードできるようになったので,kagome の辞書に加工して少し遊んでみました. 以下,公式情報ではない&開発で変更されるかもしれないので,正しいところは元コードを当たってください.

辞書の構造

辞書構造は UniDic とほぼ同じです.

カラム 内容 備考
0 headword 新国立美術館 正規化された見出し語.アルファベットが小文字に統制されてるとか
1 left-id 5962 -1が入ってることがあるが,その場合は辞書項目として無効のようだ
2 right-id 5861
3 cost 21119
4 headword 新国立美術館 こっちの見出し語は正規化されてない見出し語っぽい
5-10 part-of-speech 名詞,固有名詞,一般,,,*
11 reading form シンコクリツビジュツカン
12 normalized form 新国立美術館 動詞だったら食べ -> 食べる みたいな
13 dictionary form word id * normalized form に相当する辞書項目があればその ID が入ってるっぽい
14 分割情報 (A/B/C) C
15 A ユニット分割 2008495/1573531/2528962/2843222 数値は形態素のIDっぽいので,これで分割される形態素を引けばよさそう
16 B ユニット分割 2008495/1573531/2528997
17 word structure 2008495/1573531/2528997 これは何に使うのかな?

見出し語

Neologd からも項目を持ってきてるみたいで,結構おもしろいエントリが入ってます. たとえば,

エターナルフォースブリザード,5144,5144,7882,エターナルフォースブリザード,名詞,固有名詞,一般,*,*,*,エターナルフォースブリザード,エターナルフォースブリザード,*,C,515901/962003/982867,515901/962003/982867,515901/962003/982867

なんてのも入ってます.UniDic では短単位に切れすぎていたので,固有名詞の長い形態素とかがキレイにとれるようになるのかもしれません.Neologd は機械的にエントリを作ってるとのことなので,Sudachi に取り込まれる際にはどのような基準で取り込まれるのか,機械的にやるのか,人目でチェックするのか,などなど気になります.まだこの辺は調整されるのかもしれないので今後の辞書開発に期待です.

あと,遊ぶ上での注意点としては見出し語にユニコードリテラルがコードポイントで表現されていることがあります.

!!\u0028 ; ロ\u0029゚ ゚,5977,5977,5000,!!\u0028 ; ロ\u0029゚ ゚,記号,一般,*,*,*,*,カオモジ,!!\u0028 ; ロ\u0029゚ ゚,*,A,*,*,*

こんなエントリがありますが,\u0028ユニコードリテラルとして解釈してやる必要があります.

分割単位

Sudachi では短い方から A, B, C の3つの分割モードが提供されています. A は UniDic 短単位相当,C は固有表現相当,B は A, C の中間的な単位とのことです.

README.md に掲載されていた例を示します。

A:医薬/品/安全/管理/責任/者
B:医薬品/安全/管理/責任者
C:医薬品安全管理責任者

A:自転/車/安全/整備/士
B:自転車/安全/整備士
C:自転車安全整備士

A:消費/者/安全/調査/委員/会
B:消費者/安全/調査/委員会
C:消費者安全調査委員会

A:新/国立/美術/館
B:新/国立/美術館
C:新国立美術館

辞書では長い(分割単位BとかCの)エントリに分割情報がついているので,表示する際に分割してやれば目的を達成できそうです.

とりあえず動かしてみる

kagome は UniDic を扱えるので,UniDic の辞書だと思って Sudachi を辞書をコンパイルして動かしてみました.

お約束の すもももももももものうち です.

f:id:ikawaha:20171018180742p:plain

Sudachi には すもももももももものうち というずばりそのもののエントリもあるのですが,そちらは選ばれませんでした. Sudachi 自体で動かしてみましたが,同じ解析結果になったので,やり方を外しているわけではないみたいです.

f:id:ikawaha:20171018180638p:plain

TODO

kagome にそのまま取り込んで,分割情報で調整してやれば大体うまく動きそうな感触を得ました. kagome ではやってない Sudachi の以下の機能は別途開発しないとだめかな・・・ という感じです.

  • [ ] 入力テキスト修正
    • 文字列正規化 (全半角、大文字/小文字、異体字)
  • [ ] 長音正規化 (「~」や長音記号連続の正規化.まだリリースされてない?)
  • [ ] 未知語処理
    • 1文字未知語
  • [ ] 単語接続処理 (品詞接続禁制)
  • [ ] 出力解修正
    • カタカナ未知語まとめ上げ
    • 数詞まとめ上げ
  • [ ] 分割粒度調整 (未知語/既知語の分割粒度の平滑化.まだリリースされてない?)
  • [ ] 数詞正規化 (漢数詞や位取りの正規化.まだリリースされてない?)
  • [ ] 人名補正 (敬称や前後関係から人名部を推定.まだリリースされてない?)

Sudachi の辞書使って遊んでみたいと思ってる人の一助になれば幸いです. Happy Hacking!

goa tips: 要素がポインタになるのを避ける

はじめに

Payload の Member が必須要素でない場合,生成される対応するコードはポインタで表現されます. 文字列なら,string のポインタで表されるわけで,ポインタを渡すために変数を用意してポインタで渡してやるみたいなひと手間が必要になってきます. これはややめんどくさいので,要素がポインタになるのを避けよう・・・というものぐさな方針です.

※ もっといい方法があればしりたいです・・・

Payload や MediaType の要素がポインタになるのはどんなときか

Payload や MediaType の要素が(生成されたコードで)ポインタで表現されるのは,要素が必須要素でないときです. たとえば,下記のような Payload では,bottleIDcategory は required ですが,comment は省略可能です.

BottlePayload = Type("BottlePayload", func() {
        Member("bottleID", Integer, "Bottle ID", func(){
                Minimum(0)
                Maximum(127)
        })
        Member("category", String, "Category", func(){
                Enum("red", "whilte", "rose")
                Default("red")
        })
        Member("comment", String, "Comment", func(){
                MaxLength(256)
        })
        Required("bottleID", "category") // ← 必須要素の指定
})

生成される対応するコードは,下記のようになります.Comment*string で表現されてます.

// BottlePayload user type.
type BottlePayload struct {
        // Bottle ID
        BottleID int `form:"bottleID" json:"bottleID" xml:"bottleID"`
        // Category
        Category string `form:"category" json:"category" xml:"category"`
        // Comment
        Comment *string `form:"comment,omitempty" json:"comment,omitempty" xml:"comment,omitempty"` // ← こいつがポインタあつかい
}

必須要素にしないときの振る舞いは?

Payload で要素を必須要素にしない場合,その要素は省略できます. つまり,json で渡そうと思ったとき,

{ "bottleID" : 1, "category": "white" }

の様に(省略可能な) comment は書かなくても大丈夫です.必須要素の bottleID を省略すると,goa が生成するバリデーションではねられます.

MediaType の場合は required でなければ,ゼロ値であればレスポンスから項目が省略されます.

必須要素でも記述を省略することは可能

大体問題になるのは,Payload の要素で省略したいものがある場合だと思います. しかし,単に記述を省略したいのでしたら,必須要素にして Default DSL を利用することで,あたかも要素を省略したように記述できます.

BottlePayload = Type("BottlePayload", func() {
        Member("bottleID", Integer, "Bottle ID", func(){
                Minimum(0)
                Maximum(127)
        })
        Member("category", String, "Category", func(){
                Enum("red", "whilte", "rose")
                Default("red")
        })
        Member("comment", String, "Comment", func(){
                Default("")                       // ← ★
                MaxLength(256)
        })
        Required("bottleID", "category", "comment") // ← 必須要素の指定
})

こうしておくと,comment の値の初期値は空文字列になります.なので,

{ "bottleID" : 1, "category": "white" }

という Payload を渡しても,初期値が設定されるので,required の要件を満たします(詭弁ぽいけど). なので,初期値を設定できる要素であれば,必須要素にしておいて,Default を指定することで記述は省略可能になります.

ただし,null と空文字列を区別したいような場合にはこの方法は出来ません.

逆に言えば,null と初期値値を区別したいような場合だけ,必須要素でなくす必要がある・・・という方針でとりあえずうまくいっています.

といっても,これは goa でポインタ使いたくないと云うためのものぐさな手法で, API の作法的にどうなのかと常々疑問に思っているので,何か知見がある方いらしたらコメントいただければ嬉しいです.