VB.NETでも制約ソルバーを用いて数独を解きたい その2
はじめに
前回は地獄のSMT-LIBコピペ祭りを経て、z3で数独を解くことに成功しました。 今回はVBから制約を発行してz3に数独を解かせてみましょう。
.NETバインディング
z3には.NETのバインディングが用意されており、P/Invoke祭りやCOM祭りをやらなくともz3の機能が利用できます。やったね
まぁ、CodeDOMからコードを生成するのによく似たつらみが待ち構えているので結局アレですけど。
z3のGitHubページからライブラリをダウンロードして、Microsoft.Z3.dll
を参照に加えましょう。
また、Microsft.Z3.dll
は単なるバインディングなので、libz3.dll
も一緒にコピーしておく必要があります。
あと、libz3は(たぶん)ネイティブなので、ダウンロードしたアーキテクチャに合わせて.NETのアーキテクチャを合わせておく必要があります。 WOW64の加護により64bitOSでも32bitアプリケーションが動くので今回はx86を使用しています。 アプリケーションに割り当て可能なメモリ量の制約や若干のオーバーヘッドなどがありますが、今回は目をつぶりましょう。
余談ですが、x86のネイティブライブラリを使用しているプロジェクトで、Debugビルドはx86に直してあるのにReleaseはAnyCPUのままになっていてReleaseすると無事に64bitOSで死ぬみたいな間抜けな事があったので気を付けましょう。
数独
正直なことを申しますと、z3の公式サンプルに数独を解くコードがあるのでそちらを参考にしました。
Dim opt = New Dictionary(Of String, String) opt("proof") = "true" Using c = New Context(opt) Dim solver = c.MkSolver() Dim cells(8)() As IntExpr ' x_1_1 から x_9_9のシンボルを生成 For i = 0 To 8 Dim r(8) As IntExpr cells(i) = r For j = 0 To 8 cells(i)(j) = CType(c.MkConst(c.MkSymbol($"x_{i + 1}_{j + 1}"), c.IntSort), IntExpr) Next Next ' (assert (and (<= 1, x_?_?) (>= x_?_? 9))) For i = 0 To 8 For j = 0 To 8 solver.Assert( c.MkAnd( c.MkLe(c.MkInt(1), cells(i)(j)), c.MkLe(cells(i)(j), c.MkInt(9)))) Next Next ' (assert (distinct x_?_1 ... x_?_9)) For i = 0 To 8 solver.Assert(c.MkDistinct(cells(i))) Next ' (assert (distinct x_1_? ... x_9_?)) For j = 0 To 8 Dim col(8) As IntExpr For i = 0 To 8 col(i) = cells(i)(j) Next solver.Assert(c.MkDistinct(col)) Next ' (assert (distinct 3x3のあのマス)) For i0 = 0 To 2 For j0 = 0 To 2 Dim square(8) As IntExpr For i = 0 To 2 For j = 0 To 2 square(3 * i + j) = cells(3 * i0 + i)(3 * j0 + j) Next Next solver.Assert(c.MkDistinct(square)) Next Next Dim inst = { {0, 0, 5, 3, 0, 0, 0, 0, 0}, {8, 0, 0, 0, 0, 0, 0, 2, 0}, {0, 7, 0, 0, 1, 0, 5, 0, 0}, {4, 0, 0, 0, 0, 5, 3, 0, 0}, {0, 1, 0, 0, 7, 0, 0, 0, 6}, {0, 0, 3, 2, 0, 0, 0, 8, 0}, {0, 6, 0, 5, 0, 0, 0, 0, 9}, {0, 0, 4, 0, 0, 0, 0, 3, 0}, {0, 0, 0, 0, 0, 9, 7, 0, 0} } For i = 0 To 8 For j = 0 To 8 If inst(i, j) <> 0 Then solver.Assert(c.MkEq(c.MkInt(inst(i, j)), cells(i)(j))) End If Next Next If solver.Check() = Status.SATISFIABLE Then Dim m = solver.Model For Each i In cells For Each j In i Console.Write($"{m.Evaluate(j)} ") Next Console.WriteLine() Next Else Console.WriteLine("Failed to solve sudoku") End If End Using
おわりに
z3で数独を解くこと自体は前回の時点で出来てるので今回は特に感動もないのですが、まぁなるほどなといった感じです。 しかしまぁ、慣れは必要ですが問題を数式で表現さえ出来ればアルゴリズムを実装せずに解けるというのはかなり強力ですね。
公式サンプルではほとんどの制約を(and ...)
で連結してAssert
を2つにまで削減してました。
内部実装的にこうしたほうが早いというのがあるのかもしれませんが、今回は分かりやすさの為に条件一つ一つにAssert
を適用しました。
おわり
VB.NETでも制約ソルバーを用いて数独を解きたい その1
はじめに
マイクロソフト・リサーチが開発しているz3定理証明器に興味を持ったのでそれについてです。
鶴亀算
まずはオンラインのコンソールで鶴亀算を解いてみましょう。
z3はSMT-LIB標準形式のバージョン2を解釈できるのでそれを用いて鶴亀算の各制約を記述します。 なお、SMT-LIBはS式で記述するみたいです。 LispやSchemeを使ったことがある人ならおなじみでしょう。 おなじんでないひとは・・・がんばってください。
;; シンボルの定義 (declare-const kame Int) (declare-const tsuru Int) ;; 制約を表現 (assert (= 8 (+ kame tsuru))) (assert (= 26 (+ (* kame 4) (* tsuru 2)))) ;; 検証 (check-sat) ;; 結果の取得 (get-model)
sat (model (define-fun tsuru () Int 3) (define-fun kame () Int 5) )
数独
なんとなくで鶴亀算を解いたので、次はノリと勢いで数独を解いて行きましょう。
まずは各要素は1から9の間という制約を表現してみます。
(declare-const x_1_1 Int) (assert (and (<= 1 x_1_1) (>= 9 x_1_1))) (push) ;; --------------------------- (assert (= x_1_1 1)) (check-sat) ;; --------------------------- (pop) (push) ;; --------------------------- (assert (= x_1_1 9)) (check-sat) ;; --------------------------- (pop) (push) ;; --------------------------- (assert (= x_1_1 10)) (check-sat) ;; ---------------------------
S式がなんとなくとっつきにくいかもしれませんが、やってるのは普通の条件式ですね。
sat sat unsat
ちなみに(pop)
と(push)
はその時点でのコンテキストをスタックへプッシュ・ポップする命令です。
3行目の(push)
だったら(declare-const x_1_1 Int)
と(assert (and (<= 0 x_1_1) (>= 9 x_1_1)))
が評価された状態がスタックにプッシュされます。
次に各要素が異なるという制約を表現します。異なるという制約はdistinct
演算子を使用します。
(declare-const x_1_1 Int) (declare-const x_1_2 Int) (declare-const x_1_3 Int) (assert (distinct x_1_1 x_1_2 x_1_3)) (push) ;; --------------------------- (assert (= x_1_1 1)) (assert (= x_1_2 2)) (assert (= x_1_3 3)) (check-sat) ;; --------------------------- (pop) (push) ;; --------------------------- (assert (= x_1_1 1)) (assert (= x_1_2 2)) (assert (= x_1_3 1)) (check-sat)
sat unsat
これで必要な制約をSMT-LIBで表現する準備が出来ました。 あとは心を無にしてひたすらエディタに放り込むだけです。*1
問題はテキトーにヤフーで『数独 最高難易度』でググって出てきた結果を使用しています。
;; 01 02 03 | 04 05 06 | 07 08 09 ;; 10 11 12 | 13 14 15 | 16 17 18 ;; 19 20 21 | 22 23 24 | 25 26 27 ;; ---------+----------+--------- ;; 28 29 30 | 31 32 33 | 34 35 36 ;; 37 38 39 | 40 41 42 | 43 44 45 ;; 46 47 48 | 49 50 51 | 52 53 54 ;; ---------+----------+--------- ;; 55 56 57 | 58 59 60 | 61 62 63 ;; 64 65 66 | 67 68 69 | 70 71 72 ;; 73 74 75 | 76 77 78 | 79 80 81 ;; ;; __ __ _5 | _3 __ __ | __ __ __ ;; _8 __ __ | __ __ __ | __ _2 __ ;; __ _7 __ | __ _1 __ | _5 __ __ ;; ---------+----------+--------- ;; _4 __ __ | __ __ _5 | _3 __ __ ;; __ _1 __ | __ _7 __ | __ __ _6 ;; __ __ _3 | _2 __ __ | __ _8 __ ;; ---------+----------+--------- ;; __ _6 __ | _5 __ __ | __ __ _9 ;; __ __ _4 | __ __ __ | __ _3 __ ;; __ __ __ | __ __ _9 | _7 __ __ ;; (declare-const x01 Int) (declare-const x02 Int) (declare-const x03 Int) (declare-const x04 Int) (declare-const x05 Int) (declare-const x06 Int) (declare-const x07 Int) (declare-const x08 Int) (declare-const x09 Int) (declare-const x10 Int) (declare-const x11 Int) (declare-const x12 Int) (declare-const x13 Int) (declare-const x14 Int) (declare-const x15 Int) (declare-const x16 Int) (declare-const x17 Int) (declare-const x18 Int) (declare-const x19 Int) (declare-const x20 Int) (declare-const x21 Int) (declare-const x22 Int) (declare-const x23 Int) (declare-const x24 Int) (declare-const x25 Int) (declare-const x26 Int) (declare-const x27 Int) (declare-const x28 Int) (declare-const x29 Int) (declare-const x30 Int) (declare-const x31 Int) (declare-const x32 Int) (declare-const x33 Int) (declare-const x34 Int) (declare-const x35 Int) (declare-const x36 Int) (declare-const x37 Int) (declare-const x38 Int) (declare-const x39 Int) (declare-const x40 Int) (declare-const x41 Int) (declare-const x42 Int) (declare-const x43 Int) (declare-const x44 Int) (declare-const x45 Int) (declare-const x46 Int) (declare-const x47 Int) (declare-const x48 Int) (declare-const x49 Int) (declare-const x50 Int) (declare-const x51 Int) (declare-const x52 Int) (declare-const x53 Int) (declare-const x54 Int) (declare-const x55 Int) (declare-const x56 Int) (declare-const x57 Int) (declare-const x58 Int) (declare-const x59 Int) (declare-const x60 Int) (declare-const x61 Int) (declare-const x62 Int) (declare-const x63 Int) (declare-const x64 Int) (declare-const x65 Int) (declare-const x66 Int) (declare-const x67 Int) (declare-const x68 Int) (declare-const x69 Int) (declare-const x70 Int) (declare-const x71 Int) (declare-const x72 Int) (declare-const x73 Int) (declare-const x74 Int) (declare-const x75 Int) (declare-const x76 Int) (declare-const x77 Int) (declare-const x78 Int) (declare-const x79 Int) (declare-const x80 Int) (declare-const x81 Int) (assert (and (<= 1 x01) (>= 9 x01))) (assert (and (<= 1 x02) (>= 9 x02))) (assert (and (<= 1 x03) (>= 9 x03))) (assert (and (<= 1 x04) (>= 9 x04))) (assert (and (<= 1 x05) (>= 9 x05))) (assert (and (<= 1 x06) (>= 9 x06))) (assert (and (<= 1 x07) (>= 9 x07))) (assert (and (<= 1 x08) (>= 9 x08))) (assert (and (<= 1 x09) (>= 9 x09))) (assert (and (<= 1 x10) (>= 9 x10))) (assert (and (<= 1 x11) (>= 9 x11))) (assert (and (<= 1 x12) (>= 9 x12))) (assert (and (<= 1 x13) (>= 9 x13))) (assert (and (<= 1 x14) (>= 9 x14))) (assert (and (<= 1 x15) (>= 9 x15))) (assert (and (<= 1 x16) (>= 9 x16))) (assert (and (<= 1 x17) (>= 9 x17))) (assert (and (<= 1 x18) (>= 9 x18))) (assert (and (<= 1 x19) (>= 9 x19))) (assert (and (<= 1 x20) (>= 9 x20))) (assert (and (<= 1 x21) (>= 9 x21))) (assert (and (<= 1 x22) (>= 9 x22))) (assert (and (<= 1 x23) (>= 9 x23))) (assert (and (<= 1 x24) (>= 9 x24))) (assert (and (<= 1 x25) (>= 9 x25))) (assert (and (<= 1 x26) (>= 9 x26))) (assert (and (<= 1 x27) (>= 9 x27))) (assert (and (<= 1 x28) (>= 9 x28))) (assert (and (<= 1 x29) (>= 9 x29))) (assert (and (<= 1 x30) (>= 9 x30))) (assert (and (<= 1 x31) (>= 9 x31))) (assert (and (<= 1 x32) (>= 9 x32))) (assert (and (<= 1 x33) (>= 9 x33))) (assert (and (<= 1 x34) (>= 9 x34))) (assert (and (<= 1 x35) (>= 9 x35))) (assert (and (<= 1 x36) (>= 9 x36))) (assert (and (<= 1 x37) (>= 9 x37))) (assert (and (<= 1 x38) (>= 9 x38))) (assert (and (<= 1 x39) (>= 9 x39))) (assert (and (<= 1 x40) (>= 9 x40))) (assert (and (<= 1 x41) (>= 9 x41))) (assert (and (<= 1 x42) (>= 9 x42))) (assert (and (<= 1 x43) (>= 9 x43))) (assert (and (<= 1 x44) (>= 9 x44))) (assert (and (<= 1 x45) (>= 9 x45))) (assert (and (<= 1 x46) (>= 9 x46))) (assert (and (<= 1 x47) (>= 9 x47))) (assert (and (<= 1 x48) (>= 9 x48))) (assert (and (<= 1 x49) (>= 9 x49))) (assert (and (<= 1 x50) (>= 9 x50))) (assert (and (<= 1 x51) (>= 9 x51))) (assert (and (<= 1 x52) (>= 9 x52))) (assert (and (<= 1 x53) (>= 9 x53))) (assert (and (<= 1 x54) (>= 9 x54))) (assert (and (<= 1 x55) (>= 9 x55))) (assert (and (<= 1 x56) (>= 9 x56))) (assert (and (<= 1 x57) (>= 9 x57))) (assert (and (<= 1 x58) (>= 9 x58))) (assert (and (<= 1 x59) (>= 9 x59))) (assert (and (<= 1 x60) (>= 9 x60))) (assert (and (<= 1 x61) (>= 9 x61))) (assert (and (<= 1 x62) (>= 9 x62))) (assert (and (<= 1 x63) (>= 9 x63))) (assert (and (<= 1 x64) (>= 9 x64))) (assert (and (<= 1 x65) (>= 9 x65))) (assert (and (<= 1 x66) (>= 9 x66))) (assert (and (<= 1 x67) (>= 9 x67))) (assert (and (<= 1 x68) (>= 9 x68))) (assert (and (<= 1 x69) (>= 9 x69))) (assert (and (<= 1 x70) (>= 9 x70))) (assert (and (<= 1 x71) (>= 9 x71))) (assert (and (<= 1 x72) (>= 9 x72))) (assert (and (<= 1 x73) (>= 9 x73))) (assert (and (<= 1 x74) (>= 9 x74))) (assert (and (<= 1 x75) (>= 9 x75))) (assert (and (<= 1 x76) (>= 9 x76))) (assert (and (<= 1 x77) (>= 9 x77))) (assert (and (<= 1 x78) (>= 9 x78))) (assert (and (<= 1 x79) (>= 9 x79))) (assert (and (<= 1 x80) (>= 9 x80))) (assert (and (<= 1 x81) (>= 9 x81))) (assert (distinct x01 x02 x03 x04 x05 x06 x07 x08 x09)) (assert (distinct x10 x11 x12 x13 x14 x15 x16 x17 x18)) (assert (distinct x19 x20 x21 x22 x23 x24 x25 x26 x27)) (assert (distinct x28 x29 x30 x31 x32 x33 x34 x35 x36)) (assert (distinct x37 x38 x39 x40 x41 x42 x43 x44 x45)) (assert (distinct x46 x47 x48 x49 x50 x51 x52 x53 x54)) (assert (distinct x55 x56 x57 x58 x59 x60 x61 x62 x63)) (assert (distinct x64 x65 x66 x67 x68 x69 x70 x71 x72)) (assert (distinct x73 x74 x75 x76 x77 x78 x79 x80 x81)) (assert (distinct x01 x10 x19 x28 x37 x46 x55 x64 x73)) (assert (distinct x02 x11 x20 x29 x38 x47 x56 x65 x74)) (assert (distinct x03 x12 x21 x30 x39 x48 x57 x66 x75)) (assert (distinct x04 x13 x22 x31 x40 x49 x58 x67 x76)) (assert (distinct x05 x14 x23 x32 x41 x50 x59 x68 x77)) (assert (distinct x06 x15 x24 x33 x42 x51 x60 x69 x78)) (assert (distinct x07 x16 x25 x34 x43 x52 x61 x70 x79)) (assert (distinct x08 x17 x26 x35 x44 x53 x62 x71 x80)) (assert (distinct x09 x18 x27 x36 x45 x54 x63 x72 x81)) (assert (distinct x01 x02 x03 x10 x11 x12 x19 x20 x21)) (assert (distinct x04 x05 x06 x13 x14 x15 x22 x23 x24)) (assert (distinct x07 x08 x09 x16 x17 x18 x25 x26 x27)) (assert (distinct x28 x29 x30 x37 x38 x39 x46 x47 x48)) (assert (distinct x31 x32 x33 x40 x41 x42 x49 x50 x51)) (assert (distinct x34 x35 x36 x43 x44 x45 x52 x53 x54)) (assert (distinct x55 x56 x57 x64 x65 x66 x73 x74 x75)) (assert (distinct x58 x59 x60 x67 x68 x69 x76 x77 x78)) (assert (distinct x61 x62 x63 x70 x71 x72 x79 x80 x81)) (assert (= x03 5)) (assert (= x04 3)) (assert (= x10 8)) (assert (= x17 2)) (assert (= x20 7)) (assert (= x23 1)) (assert (= x25 5)) (assert (= x28 4)) (assert (= x33 5)) (assert (= x34 3)) (assert (= x38 1)) (assert (= x41 7)) (assert (= x45 6)) (assert (= x48 3)) (assert (= x49 2)) (assert (= x53 8)) (assert (= x56 6)) (assert (= x58 5)) (assert (= x63 9)) (assert (= x66 4)) (assert (= x71 3)) (assert (= x78 9)) (assert (= x79 7)) (check-sat) (get-model)
sat (model (define-fun x01 () Int 1) (define-fun x75 () Int 1) (define-fun x65 () Int 8) (define-fun x24 () Int 8) (define-fun x50 () Int 9) (define-fun x59 () Int 4) (define-fun x69 () Int 1) (define-fun x21 () Int 2) (define-fun x42 () Int 3) (define-fun x44 () Int 5) (define-fun x08 () Int 9) (define-fun x55 () Int 3) (define-fun x73 () Int 5) (define-fun x19 () Int 6) (define-fun x46 () Int 7) (define-fun x15 () Int 4) (define-fun x39 () Int 8) (define-fun x72 () Int 5) (define-fun x32 () Int 8) (define-fun x37 () Int 2) (define-fun x02 () Int 4) (define-fun x47 () Int 5) (define-fun x60 () Int 2) (define-fun x68 () Int 6) (define-fun x13 () Int 6) (define-fun x16 () Int 1) (define-fun x26 () Int 4) (define-fun x80 () Int 6) (define-fun x14 () Int 5) (define-fun x54 () Int 1) (define-fun x36 () Int 2) (define-fun x31 () Int 1) (define-fun x29 () Int 9) (define-fun x43 () Int 9) (define-fun x64 () Int 9) (define-fun x06 () Int 7) (define-fun x11 () Int 3) (define-fun x05 () Int 2) (define-fun x67 () Int 7) (define-fun x76 () Int 8) (define-fun x62 () Int 1) (define-fun x09 () Int 8) (define-fun x18 () Int 7) (define-fun x30 () Int 6) (define-fun x52 () Int 4) (define-fun x12 () Int 9) (define-fun x81 () Int 4) (define-fun x27 () Int 3) (define-fun x57 () Int 7) (define-fun x51 () Int 6) (define-fun x77 () Int 3) (define-fun x07 () Int 6) (define-fun x74 () Int 2) (define-fun x40 () Int 4) (define-fun x35 () Int 7) (define-fun x61 () Int 8) (define-fun x70 () Int 2) (define-fun x22 () Int 9) (define-fun x79 () Int 7) (define-fun x78 () Int 9) (define-fun x71 () Int 3) (define-fun x66 () Int 4) (define-fun x63 () Int 9) (define-fun x58 () Int 5) (define-fun x56 () Int 6) (define-fun x53 () Int 8) (define-fun x49 () Int 2) (define-fun x48 () Int 3) (define-fun x45 () Int 6) (define-fun x41 () Int 7) (define-fun x38 () Int 1) (define-fun x34 () Int 3) (define-fun x33 () Int 5) (define-fun x28 () Int 4) (define-fun x25 () Int 5) (define-fun x23 () Int 1) (define-fun x20 () Int 7) (define-fun x17 () Int 2) (define-fun x10 () Int 8) (define-fun x04 () Int 3) (define-fun x03 () Int 5) )
おわりに
やってみて思ったのですが、正直これを手入力するのはつらみポイントバクアゲなんてものではありません。 折れた心をセメントで塗り固めながら作業をしました。
定型的な制約を生成するのは手続き型と相性がよさそうです。つまり、制約の生成を手続き型にやらせて、解くのを制約ソルバーにやらせるのがよさそうです。 というわけで次回はVB.NETから制約を生成し、それをz3に放り込む方法を確認してみましょう。
VB.NETでも形態素解析っぽいことをしてExcel方眼紙から漢字を抽出したい
はじめに
皆さんは漢字は得意ですか? 弊社はクッソ苦手です。
パソコンに頼り切っちゃってるのでまぁ当たり前っちゃー当たり前だし、普段パソコンで文章を書くなら別に困りもしないのですが、打ち合わせの時に書記でホワイトボードに書くときに漢字が書けなくて困るんですよね。
そこで思ったのですが、似たような打ち合わせなら似たような漢字が出てくるのでは? つまり議事録から漢字を引っこ抜けば似たような打ち合わせに適用できるのでは?
そして議事録といえばExcel。表計算からDB設計からポスター作製までをこなすオールラウンドアプリケーション。
つまり
すればその打ち合わせで使われる漢字の傾向が掴めるって寸法です。
この時点で自分でも思っています。
素直に漢字を勉強しろよ
Excelから文字列の取り出し
とりあえずExcelからデータを抜かないといけません。
ここではEPPlusを使ってデータを取り出します。
ライブラリのインストールはNuGetからサクッとインストールしてください。
今回は特にエクセルの見た目なんてものには興味がなく、ただ文字を引っこ抜ければいいので
- ディレクトリ直下のエクセルファイルをひたすら列挙し
- すべてのシートのすべてのセルから文字列を取り出す
という感じでやってます。
Imports OfficeOpenXml Imports System.IO Public Class ExcelContentExtractor Private Sub New() ' Nothing to do. End Sub Public Shared Function ExtractContent(targetDirectory As String) As IEnumerable(Of String) Dim contents = New List(Of String) For Each it In Directory.GetFiles(targetDirectory, "*.xlsx") Using exc = New ExcelPackage(New FileInfo(it)) For Each sheet In exc.Workbook.Worksheets For Each cell In sheet.Cells() Dim text = TryCast(cell.Value, String) If Not text Is Nothing Then contents.Add(text) End If Next Next End Using Next Return contents End Function End Class
形態素解析
形態素解析はMeCabの.NETの移植版であるNMeCabを使用しています。
インストールとか使い方は完全にフィーリングでやったので解説出来ないです。ごめんね
Imports NMeCab Public Class SentenceSplitter Private Sub New() ' Nothing to do. End Sub Public Shared Function Split(sentence As IEnumerable(Of String)) As IEnumerable(Of String) Dim param = New MeCabParam() With {.DicDir = ".\ipadic"} Dim words = New List(Of String) Using mecab = MeCabTagger.Create(param) For Each it In sentence Dim node = mecab.ParseToNode(it) While Not node Is Nothing If node.Feature Is Nothing Then node = node.Next Continue While End If words.Add(node.Surface) node = node.Next End While Next End Using return words End Function End Class
このあたりを試行錯誤してる時も常に頭の片隅にありました。
素直に漢字を勉強しろよ
漢字のフィルタリング
次に漢字を含む単語をフィルタリングします。
ヤフーでググるとAscW
でUnicodeスカラ値を取り出しIf
で判断するというとっても原始的素朴でステキな方法が紹介されていました。*1
.NETの正規表現は名前付きブロックをサポートしているのでありがたくそれを使いましょう。
特に下調べしてないんで確証は無いんですがまぁCJK統合漢字を含んでれば大丈夫でしょうという感じで行きました。 好みで拡張Aもあわせてどうぞ。
Imports System.Text.RegularExpressions Public Class HanFilter Private Sub New() ' Nothing to do. End Sub Public Shared Function HasHan(word As String) As Boolean Return Regex.IsMatch(word, ".*[\p{IsCJKUnifiedIdeographs}]+.*") End Function End Class
集計
あとはLINQで一発なのでもういいや。
Module Program Sub Main(args as String()) If args.Length <> 1 Then Console.WriteLine("AppName <path of directory that contains the Excel file>") Environment.Exit(0) End If Dim contents = ExcelContentExtractor.ExtractContent(args(0)) Dim words = SentenceSplitter.Split(contents) Dim result = words. Where(AddressOf HanFilter.HasHan). GroupBy(Function(it) it, Function(it) 1). Select(Function(it) New With {.Count = it.Sum(), .Word = it.Key}). OrderByDescending(Function(it) it.Count) for each it in result Console.WriteLine("「{0}」: {1}", it.Word, it.Count) Next End Sub End Module
「漢字」: 12 「文字」: 5 「素」: 4 「形態」: 4 「解析」: 4 「打ち合わせ」: 4 「似」: 3 「議事」: 3 「録」: 3 「列」: 3 「取り出し」: 3 「弊社」: 2 「当たり前」: 2 「書く」: 2 「時」: 2 「思っ」: 2 「引っこ抜け」: 2 「抜き出し」: 2 「集計」: 2 「素直」: 2 「勉強」: 2 「特に」: 2 「感じ」: 2 「正規」: 2 「表現」: 2 「方眼」: 1 「紙」: 1 「抽出」: 1 「好き」: 1 「変数」: 1 「個」: 1 「越え」: 1 「普通」: 1 「皆さん」: 1 「得意」: 1 「苦手」: 1 「頼り」: 1 「切っ」: 1 「普段」: 1 「文章」: 1 「別に」: 1 「困り」: 1 「書記」: 1 「書け」: 1 「困る」: 1 「出」: 1 「適用」: 1 「表」: 1 「計算」: 1 「設計」: 1 「作製」: 1 「行い」: 1 「各」: 1 「要素」: 1 「使わ」: 1 「傾向」: 1 「掴める」: 1 「寸法」: 1 「時点」: 1 「自分」: 1 「抜か」: 1 「使っ」: 1 「今回」: 1 「見た目」: 1 「興味」: 1 「直下」: 1 「列挙」: 1 「取り出す」: 1 「移植」: 1 「版」: 1 「使用」: 1 「使い方」: 1 「完全」: 1 「解説」: 1 「出来」: 1 「試行」: 1 「錯誤」: 1 「常に」: 1 「頭」: 1 「片隅」: 1 「次に」: 1 「含む」: 1 「単語」: 1 「値」: 1 「判断」: 1 「原始」: 1 「的」: 1 「素朴」: 1 「方法」: 1 「紹介」: 1 「名前」: 1 「付き」: 1 「使い」: 1 「下調べ」: 1 「確証」: 1 「無い」: 1 「統合」: 1 「含ん」: 1 「大丈夫」: 1 「行き」: 1 「好み」: 1 「拡張」: 1 「一」: 1 「発」: 1
おわりに
素直に漢字を勉強しろよ
*1:しょうもないバグを作りこみそうだし、テストも面倒そう