<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>insomnia</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/" />
    <link rel="self" type="application/atom+xml" href="http://nox-insomniae.ddo.jp/insomnia/atom.xml" />
    <id>tag:nox-insomniae.ddo.jp,2012-02-12:/insomnia//2</id>
    <updated>2012-05-16T14:33:36Z</updated>
    <subtitle>二夜目の眠れない夜</subtitle>
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type Pro 5.12</generator>

<entry>
    <title>町内会</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/05/chonaikai.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1157</id>

    <published>2012-05-16T13:42:24Z</published>
    <updated>2012-05-16T14:33:36Z</updated>

    <summary>今年は町内会の班長... 12 世帯ある班のなかで毎年交代で班長を務めなければな...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="夢ノ中ノ日常" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="sports" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>今年は町内会の班長... 12 世帯ある班のなかで毎年交代で班長を務めなければならない。ゴミ収集所の掃除当番表の管理や，連絡事項の回覧，市の案内物の配布，あと集金事務が役目である。年寄りの人たちに挨拶するくらいなので，町内会の会合には，最近はまったく顔を出さなくなってしまった。</p>

<p>先日，今年分の町内会費と赤十字の募金を集めるため，ご近所巡りをやった。用意のいい妻が，「いついつ集金に伺います」とのメモをあらかじめ各家のポストに投函してあったので，きわめてスムーズに事が運んだ。会社生活が基本なので，町内会の人とはご近所の老人が亡くなったときくらいしか顔を合わせることがないわけで，「ここの主人はこんな顔してたんだ」とはじめて知るようなところもあり，近所付き合いの希薄さを改めて思い知り，なんだかちょっと情けなかった。おそらく相手の方も私の顔をはじめてみて，あああそこの子のお父さん（ウチの子供たちは体育会系で誰にでも元気よく挨拶するので，ご近所の評判がよいらしい）だと思っていたのかも知れない。</p>

<p>今日夕方，集めたお金／領収証の控えなどを町内会の役員の方に納めに行った。もう 80 をとうに越えたご老体で，お金の勘定も覚束ない感じであった。近所の資産家で，もうずっと町内会の役員を務めておられ，ご苦労さまである。</p>

<div align="center">* * *</div>

<p>先ほど，BS で AFC アジア・チャンピオンズ・リーグ，FC 東京 vs 蔚山（韓国）の試合を観た。再三の決定的チャンスを得点に結びつけられなかった FC 東京は 1-0 で敗れた。ペナルティエリア付近でつまらない FK を蔚山に与え，決勝点を献上。それなりのポゼッションをもって攻めてもゴール前でどっちらけ，つまらないミスで自滅。これが天皇杯の前回チャンピオン？ 蔚山もミスが多かった。観ていてつまらないサッカーだった。決勝トーナメント進出を決めたあとの闘いだったわけだ。観て損しました。現日本代表選手のヘボプレーを見せつけられなかっただけ，不幸中のなんとやらである。</p>]]>
        
    </content>
</entry>

<entry>
    <title>Mother&apos;s Day</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/05/mothers-day.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1156</id>

    <published>2012-05-13T10:05:13Z</published>
    <updated>2012-05-13T14:01:46Z</updated>

    <summary>今日は母の日。渋谷・代官山を散歩して，妻に午飯を馳走することにした。最近，休日に...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="cinema" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="liber" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="夢ノ中ノ日常" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>今日は母の日。渋谷・代官山を散歩して，妻に午飯を馳走することにした。最近，休日にはプログラムを書いてばかりいたので，ポカポカ陽気の外を歩きたくなった。かねてから一度行こうと思っていた代官山・蔦屋書店も覗いてみたかった。</p>

<p>代官山は，広尾と同様，外国大使館が点在し，外国人が多いためか，ハイソな趣きがあり，デザイナーなんかが好んで棲むお洒落な土地柄である。坂が多く起伏に富んでいるのも渋谷近辺の特長である。お午過ぎ，東急東横線・代官山駅に降り立つと，オープン・カフェやブティックなどの瀟洒な店が建ち並び，若いカップルで賑わっていた。この雰囲気，私には場違いな気分を禁じ得ず。</p>

<p>腹が空いていた。駅近くのビル半地下のようなところにある，「海苑」という名の中国料理レストランに入った。清朝宮廷人の官衣を飾り付けた豪華なお店だった。飲茶ランチと瓶麦酒を注文した。鷹の爪・山椒・四川唐辛子の効いた辛い麻辣スープ，海老の詰まった蒸点心三種，餠大根，太い春巻，油のさっぱりした炒飯，抹茶杏仁豆腐というメニュー。薬味も上品な味で，腹落ちもよくて（要するに，ピザやハンバーガーとは違う，「食った」感じがして）大いに満足した。コジャレたイタリアンなんかにしないでよかった。</p>

<p>山の手通りを歩いて蔦屋書店に向かう。途中，小路のブティックに見事な白のジャスミンが咲いていた。その咽せるくらいの白昼の芳香のなかで，白い野良猫が日向ぼっこをしていた。</p>

<p><a href="http://tsite.jp/daikanyama/store-service/tsutaya.html" target="_blank">代官山・蔦屋書店</a>は昨年 12 月のオープン。そのときのネットニュースで私は知った。その名の通り，レンタルチェーン TSUTAYA のお店。読んだ記事には，「大人の知的好奇心を刺激！」，「従来の『TSUTAYA』とは一線を画す，大人仕様の超充実店」とあり，少し私も興味を掻立てられた。今日訪れてみて，確かにたいへん趣味のよい店作りで，美術関係書籍も豊富，レンタル CD についても他の TSUTAYA 店舗では絶対にないくらいクラシック，ジャズが溢れており，黒とブルーの McIntosh 高級オーディオが洗練された深い音を繰り出していた。 </p>

<p>ま，書店の規模としては川崎駅のいつも行くあおい書店，丸善に及ばず，品揃えに「大人の知的好奇心を刺激」というほどのこだわりも窺えず，残念ながら，代官山のお洒落な一画にあるお洒落な店舗という見せかけで終わっている，まさに代官山に相応しい書店。『ダ・ヴィンチ』の愛読者なんかが好きそうなモードな書店。— というのが，残念ながら，貧乏人で，趣味のいかがわしい私の感想だった。</p>

<p>でも，an an や nonno，平凡パンチなど雑誌のバックナンバーに興味がある人にとっては，付属のカフェに一大コレクションがあるので，訪れてみる価値のある書店である。人出の少ない静かな夜に訪れて，コーヒーを飲みながら，備え付けの椅子で買った本を読んだり，レンタル候補 CD を視聴したりすると，ゴージャスな気分が味わえることは間違いない。このお店，深夜 2 時まで営業している。おそらくその頃合いに来店しないとこの店の本当の良さはわからないのかも知れない。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120513-daikanyama.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120513-daikanyama.png" width="606" height="456" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><u>代官山散歩: 海苑，蔦屋書店</u></div>

<div align="center">* * *</div>

<p>子供たちが録画してあった，テレビ朝日の人気ドラマ『相棒』を観た。私はこのシリーズが好きである。今回の再放送は，なかでも私のお気に入りの話だった。</p>

<p>サトエリ（佐藤江梨子）扮する，金のためなら何でもする強欲の美人・ミサエが，「和製シャーロック・ホームズ」だと勘違いして薫ちゃんを拉致・監禁し，「やまとしうるはし」の古代歌謡に基づく暗号解読を命じて，旧帝国陸軍将校の秘密結社が残した金塊を蔵するとされる金庫を開けさせようとする。強欲もキ印にまで達するかの凶暴な狂女を，サトエリがコミカルに演じていてよかった。薫ちゃんをボコスカ殴るシーンは最高である。私のサトエリ観はこの『相棒』season4 と生理用品の CM だけなんである。</p>

<p>「刃桜」なる秘密結社，暗号解読によるお宝探索など，番組のなかで右京さんの口にした通りの「荒唐無稽」感こそがミステリーの醍醐味だと思うところなので，この回のパロディー的笑いに富んだ非現実感は，ホント，私の好みなんである。真面目な『相棒』も好きなんだけど，殺人の不可解な動機，ご都合主義的事実の挿入など，真面目なだけに鼻から失笑が漏れるときもある。「荒唐無稽」のモチーフは，笑いと夢とで，こうしたわざとらしさをすべて異化してくれるのである。</p>]]>
        
    </content>
</entry>

<entry>
    <title>Russian Keyboard support</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/05/russian-keyboard-support.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1155</id>

    <published>2012-05-12T16:10:12Z</published>
    <updated>2012-05-12T17:47:17Z</updated>

    <summary>プーシキン見出語電子コンコーダンス Конкорданс к тексту А....</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="slavonic" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="日曜大工" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p><a lang="ru" href="http://yasuda.homeip.net/pushkin/lemmatized/concordance" target="_blank">プーシキン見出語電子コンコーダンス Конкорданс к тексту А. С. Пушкина</a> に，Russian Pseudo-<wbr />Keyboard ロシア語疑似キーボード機能を追加した。画面のキリル文字をクリックすると，当該文字が単語式テキスト入力エリアに挿入される。OS のインプットメソッドにロシア語を追加していないユーザでも，サービスが利用できるようになる。下図の通り，<span lang="ru" class="redmono">клавиатура</span> ボタンをクリックすると，疑似キーボード・ウィンドウがオープンする。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120512-rkb.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120512-rkb.png" width="631" height="324" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>ロシア語疑似キーボード</u></div>

<p>この疑似キーボード自体は，コンコーダンスの旧版で拵えたもので，今回は <a href="http://www.webtoolkit.eu/wt/" target="_blank">Wt C++ Web Toolkit</a> でいかに実現するかが課題だった。取りかかってみると，リンク付加オブジェクトで簡単にできてしまった。今日はそのメモ。</p>

<p><span lang="ru" class="redmono">Wt::WPushButton</span> でボタン・オブジェクトを追加し，<span lang="ru" class="redmono">setLink(link)</span> 関数によって，ロシア語キーボード画面を開く JavaScript リンクをボタンに付与した。それだけのことだった。</p>

<pre lang="ru" class="brush: cpp;" title="Wt WPushButton">
// ロシア語疑似キーボードボタン
WPushButton* button2 = new WPushButton(L"клавиатура", con);
WString kblink(WString::tr("kblink"));
button2-&gt;setLink(kblink.toUTF8());
// ロシア語疑似キーボードボタンで入力エリアにフォーカス
button2-&gt;clicked().connect(expEdit, &amp;WFormWidget::setFocus);</pre>

<p>3, 4 行目で，外部 XML ファイルの message ID <span lang="ru" class="redmono">kblink</span> に記述した JavaScript リンク（実際は <span lang="ru" class="redmono">javascript:<wbr />window.open('kbd.html', null, 'width=420, height=210, menubar=no, toolbar=no, scrollbars=yes');<wbr />void(0);</span>）を引っ張って来て，ボタンにリンクを付加している。<span lang="ru" class="redmono">kbd.html</span> とそこから呼ばれる JavaScript<span lang="ru" class="redmono">rkbd.js</span> とによって疑似キーボード機能を実現している。</p>

<p>キリル文字のボタンをキーボード配列でレイアウトしておき，そのボタンが押されたらそれに紐づいているキリル文字で，ID に対応したテキスト入力エリアの value を書き換える。仕掛けはこんなチンケなものである。だけど，同じような課題を持つ方もいるかも知れないので，これらのコードを以下に掲げておく。スタイルシートは割愛。</p>

<pre lang="ru" class="brush: xml;" title="kbd.html">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru"&gt;
  &lt;!-- -*- coding: utf-8; -*- --&gt;
  &lt;!-- $Id: kbd.html 88 2012-05-12 14:44:48Z isao $ --&gt;
  &lt;head&gt;
    &lt;meta http-equiv="Content-type" content="text/html; charset=utf-8" /&gt;
    &lt;meta name="description" content="Russian Keyboard for Concordance." /&gt;
    &lt;link rel="shortcut icon" href="favicon.ico" /&gt;
    &lt;link rel="stylesheet" type="text/css" href="kbd.css" /&gt;
    &lt;script type="text/javascript" language="JavaScript" 
        src="rkbd.js"&gt;&lt;/script&gt;
    &lt;title&gt;Russian keyboard&lt;/title&gt;
  &lt;/head&gt;
 
  &lt;body&gt;
    &lt;center&gt;
      &lt;table class="key"&gt;
        &lt;tr&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k00" value="я" 
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k01" value="ш" 
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k02" value="е" 
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k03" value="р"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k04" value="т"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k05" value="ы"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k06" value="у"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k07" value="и"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k08" value="о"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k09" value="п"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k0a" value="ю"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k0b" value="щ"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k0c" value="э"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;
    &lt;/center&gt;
 
    &lt;center&gt;
      &lt;table class="key"&gt;
        &lt;tr&gt;
          &lt;td width="10" align="center"&gt;
            &nbsp;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k10" value="а"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k11" value="с"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k12" value="д"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k13" value="ф"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k14" value="г"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k15" value="х"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k16" value="й"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k17" value="к"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k18" value="л"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k19" value="ч"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k1a" value="ж"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="30" align="center"&gt;
            &nbsp;
          &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;
    &lt;/center&gt;
 
    &lt;center&gt;
      &lt;table class="key"&gt;
        &lt;tr&gt;
          &lt;td width="20" align="center"&gt;
            &nbsp;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k20" value="з"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k21" value="ь"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k22" value="ц"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k23" value="в"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k24" value="б"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k25" value="н"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k26" value="м"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k27" value="" 
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k28" value="ъ"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="20" align="center"&gt;
            &lt;input type="button" id="k29" value="ё"
                   onClick="cin(this.value)" /&gt;
          &lt;/td&gt;
          &lt;td width="40" align="center"&gt;
            &nbsp;
          &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;
    &lt;/center&gt;
 
    &lt;center&gt;
      &lt;table class="opt"&gt;
        &lt;tr&gt;
          &lt;td width="350" align="center" valign="middle"&gt;
            &lt;span class="lbl"&gt;Регистр: &lt;/span&gt; 
            &lt;input type="radio" name="case" checked onClick="cin('+')" /&gt;
            &lt;span class="lbl"&gt;Верхний&lt;/span&gt;
            &lt;input type="radio" name="case" onClick="cin('-')" /&gt;
            &lt;span class="lbl"&gt;Нижний&lt;/span&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td width="350" align="center" valign="middle"&gt;
            &lt;span class="lbl"&gt;Переменить раскладку к&lt;/span&gt;
            &lt;input type="button" id="kt" value="Йцукен" 
                   onClick="chkb()" /&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td width="350" align="center" valign="middle"&gt;
            &lt;input type="button" id="kc" value="Стирание" 
                   onClick="cin('*')" /&gt;&nbsp;
            &lt;input type="button" value="Закрывать" 
                   onClick="window.close()" /&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;
      &lt;div class="copy"&gt;
        Copyright &copy; 2001-2012, yasuda, All Rights Reserved.
      &lt;/div&gt;
    &lt;/center&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>

<pre lang="ru" class="brush: js;" title="rkbd.js">
// -*- coding: utf-8; mode: javascript; -*- 
// Russian Pseudo-Keyboard
// Copyright(c) 2001-2012, isao yasuda, All Rights Reserved.
 
var RKstr   = opener.document.getElementById("Exps").value;
var RKc     = "";
var RKupper = false; // 大文字小文字選択
var RKtype  = true;  // キーボード配列
 
function cin(simvol) {  //set character
    RKstr = opener.document.getElementById("Exps").value;
    RKc = simvol;
    // 渡される文字によって文字挿入，オプションを切り分ける
    switch(RKc) {
    case "+":       //upper case 大文字挿入に切り替え
        RKupper = true;
        return;
    case "-":       //lower case 小文字挿入に切り替え
        RKupper = false;
        return;
    case "*":       //clear バッファのクリア
        RKstr = "";
        opener.document.getElementById("Exps").value = RKstr;
        return;
    default:        //russian symbol
        if (RKupper == true) {
            RKc = RKc.toUpperCase();
        }
        // キリル文字を入力エリアに挿入
        RKstr = RKstr + RKc;
        opener.document.getElementById("Exps").value = RKstr;
        return;
    }
}
 
function chkb() {  // キーボード配列の切り替え
    if (RKtype == false) { // qwerty (phometic)
        document.getElementById("k00").value="я";
        document.getElementById("k01").value="ш";
        document.getElementById("k02").value="е";
        document.getElementById("k03").value="р";
        document.getElementById("k04").value="т";
        document.getElementById("k05").value="ы";
        document.getElementById("k06").value="у";
        document.getElementById("k07").value="и";
        document.getElementById("k08").value="о";
        document.getElementById("k09").value="п";
        document.getElementById("k0a").value="ю";
        document.getElementById("k0b").value="щ";
        document.getElementById("k0c").value="э";
        document.getElementById("k10").value="а";
        document.getElementById("k11").value="с";
        document.getElementById("k12").value="д";
        document.getElementById("k13").value="ф";
        document.getElementById("k14").value="г";
        document.getElementById("k15").value="х";
        document.getElementById("k16").value="й";
        document.getElementById("k17").value="к";
        document.getElementById("k18").value="л";
        document.getElementById("k19").value="ч";
        document.getElementById("k1a").value="ж";
        document.getElementById("k20").value="з";
        document.getElementById("k21").value="ь";
        document.getElementById("k22").value="ц";
        document.getElementById("k23").value="в";
        document.getElementById("k24").value="б";
        document.getElementById("k25").value="н";
        document.getElementById("k26").value="м";
        document.getElementById("k27").value="";
        document.getElementById("k28").value="ъ";
        document.getElementById("k29").value="ё";
        RKtype = true;
        document.getElementById("kt").value = "Йцукен";
    } else {          // jcuken (ロシア標準配列)
        document.getElementById("k00").value="й";
        document.getElementById("k01").value="ц";
        document.getElementById("k02").value="у";
        document.getElementById("k03").value="к";
        document.getElementById("k04").value="е";
        document.getElementById("k05").value="н";
        document.getElementById("k06").value="г";
        document.getElementById("k07").value="ш";
        document.getElementById("k08").value="щ";
        document.getElementById("k09").value="з";
        document.getElementById("k0a").value="х";
        document.getElementById("k0b").value="ъ";
        document.getElementById("k0c").value="";
        document.getElementById("k10").value="ф";
        document.getElementById("k11").value="ы";
        document.getElementById("k12").value="в";
        document.getElementById("k13").value="а";
        document.getElementById("k14").value="п";
        document.getElementById("k15").value="р";
        document.getElementById("k16").value="о";
        document.getElementById("k17").value="л";
        document.getElementById("k18").value="д";
        document.getElementById("k19").value="ж";
        document.getElementById("k1a").value="э";
        document.getElementById("k20").value="я";
        document.getElementById("k21").value="ч";
        document.getElementById("k22").value="с";
        document.getElementById("k23").value="м";
        document.getElementById("k24").value="и";
        document.getElementById("k25").value="т";
        document.getElementById("k26").value="ь";
        document.getElementById("k27").value="б";
        document.getElementById("k28").value="ю";
        document.getElementById("k29").value="ё";
        RKtype = false;
        document.getElementById("kt").value = "Яшерты";
    }
    return;
}</pre>

<p>私は Mac OS X を愛用しているので，Windows ユーザのことをあんまり考えないで Web アプリを書いている。Safari が試験ブラウザである。このコンコーダンス・プログラムを Windows 7 Ultimate IE-9 で見てみたら，機能的には問題なくても，レイアウトがボロボロ。ちょっと手直しした。IE-6, 7, 8 やその他わんさかあるブラウザではどうなのか皆目不明で，恥ずかしいページを出している可能性大である。ま，切りがないので，対処はそのうち考えるということで。</p>]]>
        
    </content>
</entry>

<entry>
    <title>五月待つ</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/05/satukimatu.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1154</id>

    <published>2012-05-10T16:38:24Z</published>
    <updated>2012-05-10T18:51:01Z</updated>

    <summary>風薫る五月になった。天気はいまいち。茨城・筑波では恐ろしい竜巻の被害が出た。 五...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="liber" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="slavonic" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="夢ノ中ノ日常" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="日曜大工" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>風薫る五月になった。天気はいまいち。茨城・筑波では恐ろしい竜巻の被害が出た。</p>

<p>五月待つ花橘の香をかげば昔の人の袖の香ぞする，という古今集の名歌があって，五月というと私はすぐ，初夏の恋の匂いと懐旧心のいりまじったこの歌を思い出し，少々扇情的気分になってしまう。でも，旧暦の五月は太陽暦では六月半ばくらいなので，五月の薫風の清々しさのイメージからはかなりずれている。</p>

<div align="center">* * *</div>

<p>小沢さんが無罪になり，党員資格が復活されたと思ったら，「指定弁護士」さん，何を血迷ったのか控訴。ウソを調書に書いてブタ箱にブチ込もうとした検事が起訴されないのに，政治資金報告書に誤記したかどで控訴までされ再度被告席に立たされるこの国。一般市民による検察審査会の強制起訴は民意の反映だって？ なら，このウソツキ検事は何故に強制起訴されないのか？ 尖閣諸島海域で海上保安庁巡視船に体当たりした中国漁船船長は何故に強制起訴されないのか？ 民意だとかなんとかいうのだろうが，検察審査会なんて制度は，実は国民を舐めた制度なのである。</p>

<div align="center">* * *</div>

<p>プーチンが大統領に正式就任した。市民による反プーチンデモで運動家が逮捕されたりしたために，プーチンは独裁とのバッシングを受けている。でも，かつてのロシアを知る者がつらつら鑑みるに，反政府デモが許されるようになっただけ，ロシアも普通の国になったわけである。中国なんかに比べてずっと民主的であることを考えるべきである。デモで逮捕者が出るのは米国だって普通ではないか。日本人の私としては，give and take をきちんと弁えた政治家プーチンの大統領復帰により，北方領土交渉，エネルギー問題解決の進展に期待したい。</p>

<p>一方，フランスではサルコジさんが大統領選挙に敗れ，緊縮財政の動きにノーの民意が示された。ギリシアもしかり。欧州の財政危機はしばらく収束しそうもない。1 ユーロがいまや 100 円になろうとしている。</p>

<div align="center">* * *</div>

<p>猫ひろしさんがカンボジア国籍を取得し，ロンドン五輪マラソン・カンボジア代表に内定したのに，競技連盟かなんかの裁定で出場が取り消された。姑息な手段で五輪出場を目論でいるとの批判も大いにある。でも，国籍を変えてでも五輪やワールドカップに出たいという情熱は理解できるし，そういう方法の可能性が禁じられていない以上，猫さんを非難する理由はない。バカは彼が日本人であることをやめたことに毒づいているようだ。私からすれば，個人には国籍を変える権利があり，ナショナリズムゆえの心情がいかなるものであれ，これを理由に彼を非難してはならない。むしろ，競技連盟かなんかの裁定こそがアンフェアである。ダメならはじめからダメという規則を明らかにしておくべきなのだ。人の「正当な」（法で禁じられていないという意味で）努力を踏みにじるというものである。</p>

<div align="center">* * *</div>

<p>と，ま，<a href="http://yasuda.homeip.net/pushkin/lemmatized/concordance" target="_blank">プーシキン・コンコーダンス・プログラム新版</a>に血道を上げている間に，世の中も確実に変貌を遂げていた。ノホホンと相変わらずなオレ。阪神タイガースも相変わらずのようだけど。対戦相手がちょっとよい投手だとまったく打てない，穴だらけの「強力」打線。だから私は阪神が好き。責任感絶大でチャンスに凡退する新井選手がとくに贔屓なんである。卒のない中日なんて，トヨタやパナソニックみたいで大嫌いなのだ。私は日立やホンダのような頭の悪いメーカーが好きなのだ。</p>

<p>プーシキン・コンコーダンスに異常終了するバグがあり，原因がわからず苦労した。今日仕事から帰宅して gdb シンボリックデバッガで丹念に調査してようやく原因が判明した。コンコーダンスで条件にヒットした単語の位置情報を再帰的関数でスタックする処理があり，信じられないことだが，大量の情報，大量回数の再帰ループでスタックがパンクしてしまったのである。再帰でない方法に変更し，処理可能行数に制限をかけた（だいたい，電子コンコーダンスで数万行のコンテクスト出力を必要とするユーザはいない）。</p>

<p>先日 Facebook でこのプログラムのリンクを公開したら，作曲家・ドミトリ・スミルノフがいろいろ試験してくれ，あちこちにリンクを設置してくれ，大いに誉めてくれた。そして，何と，ロシアの有名な女流詩人であり文学研究者であるオリガ・セダコヴァ（私も彼女の二巻本選集を大切にしている）の目にも留まり，コメントをくれた。「プーシキン全作品の語彙分析ツールは，ロシアでは 4 巻本のプーシキン語彙辞典しかないのに，日本人がオンラインツールを作ってくれた」とまでうれしいことを書いてくれた。彼女がシェアしてくれたおかげで，彼女のファンから大量のアクセスが押し寄せて来た。で，上記のバグも見つけることができた。こういう一流の人たちが関心をもってくれることが，労苦の何よりの報いである。コンコーダンスの意義を理解できる人は研究者でもごくごく限られており，このプログラムは私しか使わないだろうと思っていたが，無駄ではなかったようである。</p>]]>
        
    </content>
</entry>

<entry>
    <title>Wt Web Toolkit による C++ Web プログラミング</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/05/wt-web-toolkit-c.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1153</id>

    <published>2012-05-06T08:05:52Z</published>
    <updated>2012-05-11T19:20:37Z</updated>

    <summary>プーシキン全集 Web コンコーダンス・プログラムの Web インタフェース部分...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="文房清玩" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p><a href="http://yasuda.homeip.net/pushkin/lemmatized/concordance" target="_blank">プーシキン全集 Web コンコーダンス・プログラム</a>の Web インタフェース部分は <a href="http://www.webtoolkit.eu/wt/" target="_blank">Wt C++ Web Toolkit（以下 Wt）</a>を使って拵えた。この C++ ライブラリは，Widget（画面部品オブジェクト）ベースで Web 画面を構成しつつ，ビジネスロジックも C++ で書いて行くスタイルを想定したものである。今日はコンコーダンス・プログラムで覚えた Wt のサワリ（奥が深いのでサワリに過ぎない）についてメモを残しておく。</p>

<p>最近では，Web のサーブレットを Java で作成し，ビジネスロジック（昔からある基幹システムは多く C/C++ で書かれている）とは Java JNI や CORBA，RPC，SOAP で接続し，画面周りは JSP ないしは HTML + JavaScript  ベースで準備するのが一般的のように思われる。こうすることで，Web デザインとビジネスロジックを分割して，前者を Web デザイナ，後者をプログラマが担当するというふうに，それぞれのプロフェショナルに作業分担を行うのがやりやすい，というメリットがある。私の担当システムでもこのやり方が主流である。</p>

<p>この潮流からすると，Wt のデザインパターンは一見，時代に逆行しているように私には思われた。でも実際にこれでプログラムを書いてみると，確かに HTML ほど画面構築は簡単ではないが，ヘタにデザイナに弄らせるとまずい Web フォームのような基本インタフェースを含め，コンテンツ HTML の枠組みマークアップ部分をシステム屋・プログラマに任せつつ，「見た目」のデザイン自体はスタイルシートとしてデザイナに任せ，説明文など文字コンテンツについては外部 XML の編集という形で運用者・（プログラマでない）プロジェクト要員に分担させることができる，と納得した。Wt は，総じて，極めてよく考えられたプラットフォームであることがわかったのである。いまや Web デザインは HTML ではなく CSS でこそ実現する時代なのである。</p>

<p>既存ソフトウェア資産は C/C++ が多いし，Java プログラマよりも C/C++ プログラマのほうが圧倒的に人材が豊富なので，プロジェクトの運営もしやすいと私は思う。Wt なら C++ であらゆる Web コードが実現でき，比較的高速に動作するし，C/C++ の膨大な既存資産との連携に関しても，Java よりもずっと悩みが少ない。しかも，画面 HTML，JavaScript をまったく書かずに Ajax 非同期通信の高速 Web サーバ・アプリケーションを構築できる。また，システムの作り・ビジネスロジックを完全に隠蔽することができる（どんなスタイルシートを使っているのかすらユーザにはわからないし，リンクすら見えず画像をパクられることも回避できる。システムが生成した画面の HTML から見えるのはシステムが動的に生成・送信した，わけのわからない JavaScript ばかりである）。「何でいまさら Web アプリを C++ で？」という方はぜひお試しいただきたいと思う。</p>

<p>Wt ライブラリのインストールについてはブログ記事 <a href="http://nox-insomniae.ddo.jp/insomnia/2012/04/wt-cpp-web-toolkit.html" target="_blank">Wt C++ Web Toolkit</a> にしるしたので，そちらを参照。ただし，その記事には FastCGI について記述がなかったので，もし Wt で作成した Web サーバプログラムを FastCGI で動作させたいのであれば，Wt インストールに先立って，FastCGI のデベロッパライブラリと Apache モジュールをシステムに組込んでおかなければならない。これらは <a href="http://www.fastcgi.com/drupal/" target="_blank">FastCGI のサイト</a>のダウンロードページから，それぞれ，<a href="http://www.fastcgi.com/dist/fcgi-current.tar.gz">fcgi-<wbr />current.<wbr />tar.<wbr />gz</a>，<a href="http://www.fastcgi.com/dist/mod_fastcgi-current.tar.gz">mod_<wbr />fastcgi-<wbr />current.<wbr />tar.<wbr />gz</a> を取得して，インストールする。<span class="redmono">./configure; gmake; sudo gmake install</span> で導入はしごく簡単である。その上で Wt を make すると，FastCGI 用プログラムを作成するための Wt ライブラリ <span class="redmono">libwtfcgi.<wbr />so</span>（Mac OS X の場合は <span class="redmono">libwtfcgi.<wbr />dylib</span>）がコンパイルされるはずである。</p>

<p>Wt ベースのコンコーダンス Web アプリは以下のようなものである。ただし，Wt に関係の薄いコンコーダンス・ビジネスロジック部分は省略してある。</p>

<pre class="brush: cpp;" title="concordance.cpp">
/* -*- coding: utf-8; mode: c++; -*-
 * Concordance to A. S. Pushkin's Works
 * $Id: concordance.cpp 78 2012-05-05 18:58:17Z isao $
 * Copyright (C) 2012, isao yasuda
 */
 
#include &lt;Wt/WApplication&gt;
#include &lt;Wt/WContainerWidget&gt;
#include &lt;Wt/WEnvironment&gt;
#include &lt;Wt/WLineEdit&gt;
#include &lt;Wt/WGroupBox&gt;
#include &lt;Wt/WButtonGroup&gt;
#include &lt;Wt/WRadioButton&gt;
#include &lt;Wt/WPushButton&gt;
#include &lt;Wt/WCheckBox&gt;
#include &lt;Wt/WText&gt;
#include &lt;Wt/WBreak&gt;
#include &lt;turglem/lemmatizer.hpp&gt;
#include &lt;turglem/russian/charset_adapters.hpp&gt;
#include &lt;boost/tokenizer.hpp&gt;
#include &lt;boost/locale.hpp&gt;
#include "ShmCorpusWordTree.hpp"
 
using namespace Wt;
using namespace boost::interprocess;
typedef std::pair&lt;std::string, std::string&gt; string_type;
 
/* コンコーダンス用グローバル変数，関数の記述。省略 */
 
// コンコーダンスクラス
class Concordance : public WApplication
{
    WLineEdit*    expEdit;
    WButtonGroup* wbg0;
    WButtonGroup* wbg1;
    WGroupBox*    gbox;
    WCheckBox    *g0, *g1, *g2, *g3, *g4, *g5, *g6, *g7,
        *g8, *g9, *g10, *g11, *g12; // 00-11 + all
    WText* wmesg;
    WText* pmesg;
    WText* cmesg;
    void clearvse();
    void clearother();
    void generate();
    void errorsend(const std::string emsg);
public:
    Concordance(const WEnvironment& env);
};
 
Concordance::Concordance(const WEnvironment& env)
    : WApplication(env)
{
    // メッセージファイル message.xml の指定
    messageResourceBundle().use(appRoot() + "message");
    // タイトル
    setTitle(L"Динамический Конкорданс к сочинениям А. С. Пушкина");
    // スタイルシート
    useStyleSheet("style.css");
    // message.xml から header を出力
    root()-&gt;addWidget(new WText(WString::tr("header"), XHTMLText));
    root()-&gt;addWidget(new WText(WString::tr("subheader"), XHTMLText));
    root()-&gt;addWidget(new WBreak());
 
    // 入力プロンプト(初期値付き))
    root()-&gt;addWidget(new WText(L"Выражения ", XHTMLText));
    // expEdit = new WLineEdit(L"^П[ЕЁ]СТР.*", root());
    // 入力エリア: Enter key で submit 
    expEdit = new WLineEdit(root());
    expEdit-&gt;setEmptyText
        (L"Вводите Слова или Регулярные выражения, напр.: п[её]стр.*");
    expEdit-&gt;setTextSize(70);
    // expEdit-&gt;setFocus(); // focus より説明テキスト
    expEdit-&gt;enterPressed().connect(this, &Concordance::generate);
    // submit ボタン
    WPushButton *button = new WPushButton(L"поиск", root());
    button-&gt;setMargin(10, Left);
    // ジャンル一覧へのリンク(message.xml)
    root()-&gt;addWidget(new WText(WString::tr("link"), XHTMLText));
    root()-&gt;addWidget(new WBreak());
 
    // 見出語形／出現形選択ラジオボタン
    WGroupBox *gbox0 = new WGroupBox(L"Форма Базы данных", root());
    gbox0-&gt;setId("gbx0");
    wbg0 = new WButtonGroup(gbox0);
    WRadioButton *rb0;
    rb0 = new WRadioButton(L"Lemmatized (форма заглавных слов)", gbox0);
    wbg0-&gt;addButton(rb0, 0);
    rb0 = new WRadioButton(L"Non Lemmatized (форма в тексте)", gbox0);
    wbg0-&gt;addButton(rb0, 1);
    wbg0-&gt;setCheckedButton(wbg0-&gt;button(0)); // Lemmatized
 
    // 入力を見出語変換(見出語形の場合のみ)
    WGroupBox *gbox1 = new WGroupBox
        (L"Лемматизация входа", root());
    gbox1-&gt;setId("gbx1");
    wbg1 = new WButtonGroup(gbox1);
    WRadioButton *rb1;
    rb1 = new WRadioButton(L"ON (только в случае Lemmatized)", gbox1);
    wbg1-&gt;addButton(rb1, 0);
    rb1 = new WRadioButton(L"OFF", gbox1);
    wbg1-&gt;addButton(rb1, 1);
    wbg1-&gt;setCheckedButton(wbg1-&gt;button(0)); // YES
 
    // ジャンル選択チェックボックス
    gbox = new WGroupBox(L"Выбор жанров", root());
    gbox-&gt;setId("gbx");
    g12 = new WCheckBox(L"Все", gbox);
    g0  = new WCheckBox(L"Стихи", gbox);
    g1  = new WCheckBox(L"Поэмы", gbox);
    g2  = new WCheckBox(L"Сказки", gbox);
    // その他チェックボックス設定は省略
    // ジャンルデフォルト: すべて
    g12-&gt;setChecked(true);
    // チェック時イベント処理登録
    g0-&gt;checked().connect(this,  &Concordance::clearvse);
    g1-&gt;checked().connect(this,  &Concordance::clearvse);
    g2-&gt;checked().connect(this,  &Concordance::clearvse);
    // その他チェックボックス設定は省略
    g12-&gt;checked().connect(this, &Concordance::clearother);
 
    // ラベル表示，ユーザ入力内容表示エリア
    wmesg = new WText(root());
    pmesg = new WText(root());
    root()-&gt;addWidget(new WBreak());
 
    // コンコーダンス出力エリア
    cmesg = new WText(root());
    root()-&gt;addWidget(new WBreak());
 
    // フッタを message.xml から出力
    root()-&gt;addWidget(new WText(WString::tr("footer"), XHTMLText));
    root()-&gt;addWidget(new WBreak());
 
    // 入力内容で submit
    button-&gt;clicked().connect(this, &Concordance::generate);
}
 
// エラーメッセージ出力
void Concordance::errorsend(const std::string emsg)
{
    WString s(emsg, UTF8);
    s = "&lt;p class=\"err\"&gt;" + s + "&lt;/p&gt;";
    cmesg-&gt;setTextFormat(XHTMLUnsafeText);
    cmesg-&gt;setText(s);
}
 
// オール以外クリア
void Concordance::clearother()
{
    // オール指定がチェックされていたらほかをクリア
    if (g12-&gt;isChecked()) {
        g0-&gt;setChecked(false); g1-&gt;setChecked(false); g2-&gt;setChecked(false);
        // その他設定省略
    }
}
 
// オール指定クリア
void Concordance::clearvse()
{
    // オール指定以外がチェックされていたらオール指定をクリア
    if (g0-&gt;isChecked())  g12-&gt;setChecked(false);
    if (g1-&gt;isChecked())  g12-&gt;setChecked(false);
    // その他設定省略
}
 
// コンコーダンス生成
void Concordance::generate()
{
    const WEnvironment& env = WApplication::instance()-&gt;environment();
    // ユーザ入力単語式: WT::WString を std::string に toUTF() で変換
    std::string ldata = (expEdit-&gt;text()).toUTF8(); // 元入力
    std::string userip = "IP: " + env.clientAddress() + " Req: " + ldata;
    logging(userip.c_str(), 0);
 
    // 大文字変換
    std::string udata = boost::locale::to_upper(ldata);
 
    // 実行 Word Tree の選択，Lemmatizer 実行可否判断
    offset_ptr&lt;tree&gt; word_tree; // 実行 Word Tree
    WString opts(ldata, UTF8);
    opts = L" (Ваш вход: " + opts;
    // Lem Tree on なら見出語形ツリー選択
    if (wbg0-&gt;checkedId() == 0) {
        word_tree = word_tree_l; // select Lemmatized 
        opts += L"; DB: Lemmatized; Лемматизация входа: ON)";
        // Lemmatizing on なら Lemmatizer で入力を見出語変換する
        if (wbg1-&gt;checkedId() == 0) {
            lemmatizer(udata);   // 見出語変換
        }
    } else {
        // off なら出現形ツリー選択
        word_tree = word_tree_a; // select Non Lemmatized
        opts += L"; DB: Non Lemmatized; Лемматизация входа: OFF)";
    }
 
    // ユーザ入力をトークナイザで分解し，式を vector にセット
    std::vector&lt;std::string&gt; wds;
    exp_tokenizer(udata, wds);
    if (wds.empty()) {
        errorsend("No expressions specified.");
        return;
    }
 
    // 対象ジャンルのセット: global int vector にセット
    // - g0 から順にチェックを確認し，true なら push_back
    // - 全選択がチェックされている場合は vector を空にする
    // - ひとつも選択がない場合は全選択となる
    genrev.clear();
    if (g0-&gt;isChecked())  genrev.push_back(0);
    if (g1-&gt;isChecked())  genrev.push_back(1);
    if (g2-&gt;isChecked())  genrev.push_back(2);
    // 中略
    if (g12-&gt;isChecked()) genrev.clear();
    if (genrev.empty())
        grall = true;
    else
        grall = false;
 
    // ラベルの出力
    wmesg-&gt;setTextFormat(XHTMLText);
    wmesg-&gt;setText(L"&lt;div class=\"blk\"&gt;&lt;/div&gt;&lt;br /&gt;Выражения: ");
 
    // ユーザ入力内容の表示
    // 変換済ユーザ入力
    WString wdata(udata, UTF8);
    wdata += opts;
    pmesg-&gt;setTextFormat(PlainText);
    pmesg-&gt;setStyleClass("exp");
    pmesg-&gt;setText(wdata);
 
    /* 以下にビジネスロジック cmesg に出力コンコーダンスを格納する。省略 */
 
    // コンコーダンスの出力
    cmesg-&gt;setTextFormat(XHTMLUnsafeText);
    cmesg-&gt;setText(conc);
}
 
// サービスオブジェクトの生成
WApplication *createApplication(const WEnvironment& env)
{
    return new Concordance(env);
}
 
// main: 各種 Web パラメータとともにサービスオブジェクトを生成する
int main(int argc, char** argv)
{
    /* Web サービスの初期処理を行う。省略 */
    // サービス開始
    return WRun(argc, argv, &createApplication);
}</pre>

<p>このように，<span lang="ru" class="redmono">Concordance</span> クラスとそのクラスメソッドとして，画面，ビジネスロジックを記述して行く。<span lang="ru" class="redmono">WApplication</span> クラス継承として <span lang="ru" class="redmono">Concordance</span> クラスを生成し，<span lang="ru" class="redmono">Wt::<wbr />WServer::<wbr />WRun()</span> メソッドでサービスを開始する形態である。</p>

<p>プーシキン・コンコーダンス画面では，テキストボックス，送信ボタン，チェックボックス，ラジオボタンをコンコーダンス・ビジネスロジック動作条件の設定のために用いている。これらの画面要素を <span lang="ru" class="redmono">Concordance</span> クラスのコンストラクタのなかで Wt Widget オブジェクトとして定義している。 画面の「地」は，<span lang="ru" class="redmono">WApplication</span> クラスのメソッドである <span lang="ru" class="redmono">root()</span> によってオブジェクトのポインタが返されるようになっていて，これに紐付けて各種 Widget を <span lang="ru" class="redmono">new</span> 演算子で生成して行く。</p>

<p>上例では，チェックボックスやラジオボタンを <span lang="ru" class="redmono">WGroupBox</span>，<span lang="ru" class="redmono">WButtomGroup</span> といった，ボタンやチェックボックスを纏める Widget のなかに入れており，この場合は <span lang="ru" class="redmono">root()</span> にバインドされたこれらグループに紐付けてボタン等の Widget を生成する。 <span lang="ru" class="redmono">root()</span> に付随するすべての画面オブジェクトは，プログラムが終了する時点で再帰的に解放されるので，プログラマがポインタを管理し <span lang="ru" class="redmono">delete</span> を発行する必要はない。提供 Widget，インクルードすべきヘッダ等，その使い方の詳細は <a href="http://www.webtoolkit.eu/wt/doc/reference/html/index.html" target="_blank">Wt ドキュメント</a>，<a href="http://www.webtoolkit.eu/widgets" target="_blank">Widget Gallery デモプログラム</a>を参照のこと。</p>

<p>テキストデータは <span lang="ru" class="redmono">Wt::WString</span> クラスオブジェクトとして扱う必要がある。<span lang="ru" class="redmono">Wt::<wbr />WString(char* value, UTF8)</span> のように初期化すれば，<span lang="ru" class="redmono">std::<wbr />string</span> や <span lang="ru" class="redmono">char*</span> の UTF-8 文字列からも，<span lang="ru" class="redmono">Wt::<wbr />WString</span> オブジェクトが得られる。<span lang="ru" class="redmono">UTF8</span> という指定は，UTF-8 エンコードデータとして <span lang="ru" class="redmono">Wt::<wbr />WString</span> に格納する旨の指示である。直接 UTF-8 文字列を格納したい場合は <span lang="ru" class="redmono">Wt::<wbr />WString<wbr />(L"Выражения")</span> のようにワイド文字定数として書けばよい。ソースコード中の文字列のエンコーディングの扱いはコンパイラ依存ということになっているが，最近の GNU C/C++ Version 4 では UTF-8 として扱うはずである。もしそうでないコンパイラを使っていたり，移植性を重視するなら，<span lang="ru" class="redmono">\u十六進数</span> 記法によってユニバーサル文字定数を書けばよい。</p>

<p><span lang="ru" class="redmono">useStyleSheet("スタイルシートファイル名")</span> でスタイルシートの指定ができる。Widget オブジェクトに対し，<span lang="ru" class="redmono">setClass()</span>，<span lang="ru" class="redmono">setId()</span> メソッドで class や id の HTML 要素を付加でき，「見た目」のコントールを指定したスタイルシートで記述することが可能である。</p>

<p>上例の <span lang="ru" class="redmono">messageResourceBundle().use(appRoot() + "message");</span> 行は，外部 XML ファイルリソースの指定である。この場合 <span lang="ru" class="redmono">message.<wbr />xml</span> という XML ファイル（名称は任意）を指定している。このファイルに，</p>

<pre class="brush: xml; gutter: false;" title="message.xml">
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!-- 
message.xml: コンコーダンス用メッセージファイル
--&gt;
&lt;messages&gt;
  &lt;message id="header"&gt;
    &lt;div id="header"&gt;
      &lt;h1&gt;Конкорданс к тексту А. С. Пушкина&lt;/h1&gt;
    &lt;/div&gt;
  &lt;/message&gt;
  &lt;!-- 後略 --&gt;
&lt;/messages&gt;</pre>

<p>と記述しておくと，<span lang="ru" class="redmono">Wt::<wbr />WString::<wbr />tr<wbr />("header")</span> によって，<span lang="ru" class="redmono">message.<wbr />xml</span> ファイルの <span lang="ru" class="redmono">header</span> という ID 属性をもつ <span lang="ru" class="redmono">message</span> タグのテキストが <span lang="ru" class="redmono">Wt::<wbr />WString</span> オブジェクトとして与えられ，これを Web 画面に挿入することができる。こうして，画面のテキストコンテンツを別ファイルで管理でき，文言修正のためにプログラム改修しなければならない不都合を回避できる。これらスタイルシートや XML はプログラム動作中でも，修正がすぐ反映される。また，画面ソースなどを通してユーザから見えることはないので，コンテンツのその他の文言や画像リンクなどをユーザから隠蔽することが可能である。</p>

<p>Wt ではアプリケーションを動作させる環境として二種類をサポートしている。ひとつは Wthttpd という独自のスタンドアロン Web Socket サーバとして，いまひとつは Apache2 等の Web サーバから起動する FastCGI のアプリケーションとして動作させることができる。この際，ソースコードはまったく同じであり，これらの違いはリンケージするとき，<span lang="ru" class="redmono">libwthttp</span> と <span lang="ru" class="redmono">libwtfcgi</span> のどちらを使うかに依存する。<a href="http://yasuda.homeip.net/pushkin/lemmatized/concordance" target="_blank">プーシキン・コンコーダンス</a>の場合は FastCGI で動作させているので，コンパイル／リンクは以下のとおりである。</p>

<pre class="brush: term; gutter: false;" title="compile and link">
% g++ -g -I/usr/local/include -L/usr/local/lib \
  -boost_random -lboost_signals -lboost_system -lboost_thread \
  -boost_filesystem -lboost_program_opions -lboost_date_time \
  -lboost_regex -lboost_locale \
  -licudata -licui18n -licuuc \
  -lwtfcgi -lwt \
  -o concordance concordance.cpp \
  -lturglem -lturglem-russian -lMAFSA</pre>

<p><span lang="ru" class="redmono">-lwtfcgi -lwt</span> のライブラリ・リンケージ指定が FastCGI 運用の条件である。Boost の各種ライブラリも Wt に必要である（すべてというわけではない）。ICU Unicode ライブラリはコンコーダンスのロシア語正規表現で使うもの。最後の三つのライブラリは Lemmatizer である。</p>

<p>FastCGI を Apache から使えるようにするには，Apache の <span lang="ru" class="redmono">httpd.conf</span> に，モジュールを追加するだけでよい。モジュールのパスはインストールしたところを指すようにする。</p>

<pre class="brush: plain; gutter: false;" title="httpd.conf">
LoadModule fastcgi_module  libexec/apache22/mod_fastcgi.so</pre>

<p>Wt Web アプリ FastCGI を動かす以下のような設定を外部ファイル（<span lang="ru" class="redmono">fastcgi.<wbr />conf</span>）に記述し，Apache が読込む構成ファイルディレクトリに格納する。<span lang="ru" class="redmono">Alias</span> の指定は必須ではないが，サイトのドキュメントルートからアクセスできるマッピングをしておかなくては，実質利用できない。</p>

<pre class="brush: plain; gutter: false;" title="fastcgi.conf">
&lt;IfModule mod_fastcgi.c&gt;
    Alias /pushkin/lemmatized/ /home/isao/src/pushkin/concordance/
    FastCgiServer /home/isao/src/pushkin/concordance/concordance
&lt;/IfModule&gt;</pre>

<p>上のコンコーダンス・プログラム例でいえば，<span lang="ru" class="redmono">/home/<wbr />isao/<wbr />src/<wbr />pushkin/<wbr />concordance/</span> ディレクトリ（アプリケーションディレクトリ）下にロードモジュール，<span lang="ru" class="redmono">messeage.<wbr />xml</span>，<span lang="ru" class="redmono">style.<wbr />css</span> などのリソースを格納しておく。あと，Wt Widget がデフォルトで参照するスタイルシートや画像ファイルが Wt パッケージの <span lang="ru" class="redmono">resources</span> ディレクトリにあるので，これをアプリケーションディレクトリ内にシンボリックリンクしておくとよい。</p>

<p>Apache をリスタートすればアプリが動くはずである。Apache エラーログ <span lang="ru" class="redmono">httpd-<wbr />error.<wbr />log</span> に FastCGI 関連の起動メッセージが出力されるので，正しく動きはじめたかがわかる。ここで <span lang="ru" class="redmono">Permission denied</span> のようなエラーが出て異常終了しているなら，<span lang="ru" class="redmono">/var/run/wt</span> ディレクトリに Web httpd のユーザ（www, _www など）による書込権限が付与されているか確認するとよい。</p>

<p>FastCGI ではなく Wthttpd で動かすのなら，リンクライブラリを <span lang="ru" class="redmono">-lwthttp -lwt</span> とすればよい。そして，以下のようにプログラムを起動する。</p>

<pre class="brush: term; gutter: false;" title="wthttpd">
% concordance (プログラムロードモジュール名) --docroot . \
  --http-address 0.0.0.0 --http-port 8080 &amp;</pre>

<p>無事起動できれば，<span lang="ru" class="redmono">http://<wbr />host:<wbr />8080/</span> URL でブラウザからアクセスできるようになる。</p>

<p>Wt では <span lang="ru" class="redmono">/etc/<wbr />wt/<wbr />wt-<wbr />config.<wbr />xml</span> にサーバ動作設定を記述するようになっている。wthttpd の引数指定ともども，詳細は <a href="http://www.webtoolkit.eu/wt/doc/reference/html/index.html" target="_blank">Wt ドキュメント</a>を参照いただきたい。Wt には SVG，PDF を生成する関数なども用意されており，HTML5 にも追従している。これまで C++ Web アプリを CGI のとろいインタフェースにしか結びつけられなかったシステム設計者には，Wt は驚きのライブラリである。</p>]]>
        
    </content>
</entry>

<entry>
    <title>Конкорданс к тексту А. С. Пушкина Completed</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/05/pushkin-concordance-completed.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1152</id>

    <published>2012-05-03T15:25:43Z</published>
    <updated>2012-05-06T06:49:32Z</updated>

    <summary>プーシキン・コンコーダンス Web サービスに全コーパスを搭載した。これは，ロシ...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="liber" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="slavonic" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="日曜大工" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>プーシキン・コンコーダンス Web サービスに全コーパスを搭載した。これは，ロシアの文学電子テキストサイト <a lang="ru" href="http://www.rvb.ru/pushkin/" target="_blank">Русская виртуальная библиотека (RVB)</a> から <span lang="ru">А. С. Пушкин. Полное собрание сочинений в десяти томах.</span> = 10 巻本プーシキン全集の HTML テキストを Wget でダウンロードし，コーパスとして加工したものに基づいている。RVB のテキストは 1959 年にモスクワで出版されたアカデミー版プーシキン全集に従って電子化されたものである。</p>

<p>RVB HTML のテキスト誤り（主にキリル文字を同形のラテン文字で誤植したもの。あまりの夥しさにびっくりした）を訂正し，プーシキンテクスト以外の校訂者テクスト等の余計な部分を削った。作品テクストについては，タイトル，章・節名を外し，プーシキンによる注，序文のほか，エピグラフやプーシキンによる他の作家からの引用は取り込んである。KWIC コンコーダンス表から該当テクストを表示するために，行・パラグラフごとに HTML ID 要素として独自に行 ID を付加した。Perl <span class="redmono">HTML::Parser</span> モジュールによって，RVB HTML データをタグ解析した上で抽出したプーシキン・テクストに，ジャンルID，作品番号（作品名データベースのキーであると同時に Web ページのファイルベース名でもある），行番号，行ID を付与して，コーパスを作成した。</p>

<p>共有メモリへのデータ構築，ロシア語形態素解析ライブラリ Lemmatizer の活用（出現形からの見出語解析），ロシア語正規表現操作，UTF-8 文字列操作（大文字変換など）等々，C/C++ 言語ノウハウにかなり悩まされたのだけれど，今回何より時間を掛けたのはコーパスの準備であった。サービス用の C++ プログラムは 1500 行にも満たないが，コーパス編集のために試行錯誤で書いた Perl プログラム，シェルスクリプトはその 2 倍にはなると思う。テキストエディタ上で手動でしこしこ手直ししたりもしなければならなかった。思い立って 3 年くらいだろうか，ようやく目的のことができるまで漕ぎ着けたという感がある。</p>

<p>前回からの改変を整理しておく。プーシキン全集コーパス（ただしヴァリアントはまだ未収録である）を掲載するにあたり，コンコーダンス生成対象ジャンルを選択できるようにした。そして，どの作品がジャンルに属しているのかが一覧できる Web ページを準備し，そのリンクを設置した。見栄えの変更を少々行った。改めて，使い方を説明以下に説明する。</p>

<p>プーシキン・コンコーダンス・ページは <a href="http://yasuda.homeip.net/pushkin/lemmatized/concordance" target="_blank">http://yasuda.<wbr />homeip.<wbr />net/<wbr />pushkin/<wbr />lemmatized/<wbr />concordance</a> である。初期画面は図 1. の通りである。（5.5 付記：URL を改変した。）</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120505-conc-winit.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120505-conc-winit.png" width="686" height="567" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>図 1. 初期画面</u></div>

<p><span lang="ru">Вводите Слова или Регулярные выражения, напр.: п[её]стр.*</span> と書かれたテキストボックスに，KWIC コンコーダンス表を生成したい単語条件を入力する。そして，オプションのチェックボックスでジャンルを選択する。初期状態では <span lang="ru" class="redmono">все</span> がチェックされている。ジャンルにどの作品が属しているかは実行ボタン右にある <span lang="ru">Жанровые разделы конкорданса</span> ページ（図 2.）で確認できる。このページのジャンル（<span lang="ru">Стихитворения</span> 等）の左側にある番号は，ジャンルID（<span lang="ru" class="redmono">00-12</span>）である。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120503-conc-wrad.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120503-conc-wrad.png" width="686" height="544" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>図 2. ジャンル分類ページ</u></div>

<p>単語条件は空白文字で区切って複数指定できる。単語の見出語に完全一致する形式で入力してもよいが，正規表現を用いて条件に幅をもたせることをお勧めする。メタ文字 — 任意の一文字にマッチする <span lang="ru" class="redmono">.</span> ピリオド，前置文字の 0 個以上にマッチする <span lang="ru" class="redmono">*</span> アスタリスク，指定した文字のどれかにマッチするグループを形成する <span lang="ru" class="redmono">[ ]</span>，語頭，語末にそれぞれマッチする <span lang="ru" class="redmono">^$</span> 等 — を組み合わせて単語条件を表現するのである。メタ文字が一つも含まれないと完全一致条件とみなす。</p>

<p>この正規表現のマッチングには Boost.<wbr />Regex C++ ライブラリを用いており，<a href="http://www.google.co.jp/url?q=http://ja.wikipedia.org/wiki/%25E6%25AD%25A3%25E8%25A6%258F%25E8%25A1%25A8%25E7%258F%25BE&sa=U&ei=2KyiT_vxFanGmQX6n6jaBw&ved=0CBQQFjAA&sig2=CoglOJeJ8FS2KRDbsy61JA&usg=AFQjCNFeW9G3ap4JzJg8uLV7wUHUb_SJ1A" target="_blank">正規表現 Wikipedia</a> に述べられた大概のものは使えると思う。正規表現で <span lang="ru" class="redmono">.*</span> と指定すると，任意の一文字とその繰返し，すなわち全単語の指定と同等になるが，システム占有防止のため，メタ文字だけからなる単語条件指定は受け付けない仕様にしてある。</p>

<p>単語条件には近接隣接演算指定ができる。これは目的とする語について別のある語と離れている距離が，指定した語数，行数の範囲内であるという条件である。<span lang="ru" class="redmono">単語1&lt;W|L数値&gt;単語2</span> の書式である。両端の<span lang="ru" class="redmono">単語1, 2</span> は正規表現でもよい。例えば，<span lang="ru" class="redmono">СЛОВО1&lt;W10&gt;СЛОВО2</span> のように空白文字を入れずに書くと，<span lang="ru" class="redmono">СЛОВО2</span> との距離が 10 語以内の <span lang="ru" class="redmono">СЛОВО1</span> を探索して KWIC を生成する。<span lang="ru" class="redmono">W</span> の代わりに <span lang="ru" class="redmono">L</span> を指定すると行条件となる。ここで行とは詩の場合は詩行だが，散文についてはパラグラフと考えてよい。</p>

<p>近接隣接演算 <span lang="ru" class="redmono">пестр.*&lt;W5&gt;приня.*</span> 指定を行った例を図 3. に示す。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120505-conc-wex.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120505-conc-wex.png" width="686" height="567" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>図 3. 近接隣接演算結果</u></div>

<p><span lang="ru" class="redmono">приня.*</span>（<span lang="ru" class="redmono">приня</span> という部分文字列）を含む単語から 5 語以内に出現する <span lang="ru" class="redmono">пестр.*</span>（<span lang="ru" class="redmono">пестр</span> という部分文字列を含む単語）を探索している。結果は <span lang="ru" class="redmono">ПЕСТРЫЙ</span> 1 件だったことが赤地の行でわかる。それに続いて，ヒットした語の行及び前後行からなるコンテキストが表示される。<span lang="ru" class="redmono">певтрых</span> が <span lang="ru" class="redmono">Прими</span> の 2 語あとに出現していて，条件に合致している（<span lang="ru" class="redmono">прими</span> の見出語は <span lang="ru" class="redmono">принять</span> なので <span lang="ru" class="redmono">приня.*</span> の条件でヒットするのである）。ヒットした語は赤ボールドイタリック体で，ヒットした行は薄紅背景で強調される。</p>

<p>コンテクスト右端に表示されているのは位置情報である。<span lang="ru" class="redmono">ジャンルID:作品番号:出現行</span> の形式である。位置情報にカーソルを合わせると，その作品名がポップアップされる。位置情報にはリンクが設置されており，クリックすると，当該作品の Web ページのヒットした行を直接参照することができる（図 4.）。作品番号は本システム内の便宜上のものに過ぎない。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120503-conc-link.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120503-conc-link.png" width="686" height="496" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>図 4. 位置情報リンク</u></div>

<p>オプション <span lang="ru" class="redmono">Форма Базы данных</span> は，指定単語条件の探索を見出語形で行う（見出語形モード）か出現形で行う（出現形モード）かの指定である（図 5.）。<span lang="ru" class="redmono">Lemmatized (форма заглавных слов)</span> は Lemmatizer によって見出語に正規化した語形に出現単語を纏めて KWIC コンコーダンス表を作成する。これが初期状態で ON になっている。見出語形式では，入力にも見出語を表現する条件式を指定しないとヒットしない。単語の変化形を完全一致で入力しても，データベースのキーが見出語形であるためである。見出語での整理にこだわらず出現形そのもので探索したいのならば <span lang="ru" class="redmono">Non Lemmatized (форма в тексте)</span> を選択すればよい。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120505-conc-opt1.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120505-conc-opt1.png" width="514" height="56" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>図 5. 見出語形モード／出現形モード選択オプション</u></div>

<p>オプション <span lang="ru" class="redmono">Лемматизация входа</span> は，データベースを探索する前にユーザ入力を見出語形式に変換するか否かの指定である（図 6.）。この指定は見出語形式の場合のみ参照される。前述のとおり，見出語モードの場合，例えば <span lang="ru" class="redmono">пестрых</span> という形容詞複数生格を入力してしまうと見出語形ではないのでヒットせず無意味である。このオプションを <span lang="ru" class="redmono">ON (только в случае Lemmatized)</span> にしておくと，システムがユーザ入力を見出語形に変換してからデータベース探索を行う。ただし正規表現メタ文字があるとこの変換はなされない。オプションが <span lang="ru" class="redmono">OFF</span> だとユーザ入力のまま探索する。初期状態は ON である。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120505-conc-opt2.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120505-conc-opt2.png" width="312" height="56" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>図 6. 入力見出語変換選択オプション</u></div>

<p>本システムにおける見出語への正規化は，<a href="http://lemmatizer.org/" target="_blank" lang="ru">Lemmatizer: Лемматизатор европейских языков</a> を用いている。ロシア語の見出語自動生成は複数の選択肢がある場合が多く，文脈に応じた完璧な見出語選定は不可能である。本システムにおける見出語選定方法については，本ブログ記事 <a href="http://nox-insomniae.ddo.jp/insomnia/2012/04/russian-word-lemmatizer-prototyping.html" target="_blank">Russian Word Lemmatizer</a> を参照いただきたい。</p>

<p>使い方概略は以上である。ヴァリアント以外のあらゆるプーシキン・テクストをベースにしている。考えられる限り最大限の高速化を図ったつもりである。プーシキン研究者のお役に立てることを願っている。ご意見，ご要望があればぜひ電子メールでご連絡いただきたい。ただし，当座は試験的運用として一般公開するが，サーバ・アタックが最近あまりにも多いので，いずれセキュリティ防御を施して限定公開とするつもりである。</p>

<p>今回の改修で，機能的にはほぼ目的としたところまでできた。今回，Web の画面構築，バックエンドのビジネスロジックなど，すべてを <a href="http://www.webtoolkit.eu/wt/" target="_blank">Wt C++ Web Toolkit</a> を使って実現した。Apache2 とのインタフェースでは <a href="http://www.fastcgi.com/drupal/" target="_blank">FastCGI</a> モジュールを利用して高速化を図っている。あとは閑をみて，ロシア語疑似キーボードや使用説明等のタブを設置したいと考えている。<a href="http://www.webtoolkit.eu/wt/" target="_blank">Wt C++ Web Toolkit</a> には，この実現を支援する魅力的な機能が満載なんである。</p>]]>
        
    </content>
</entry>

<entry>
    <title>Конкорданс к тексту А. С. Пушкина</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/05/web-concordance-proto.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1151</id>

    <published>2012-05-01T05:56:17Z</published>
    <updated>2012-05-06T06:48:34Z</updated>

    <summary>プーシキン・コンコーダンス・プロトタイプを Web に搭載してみた。Wt C++...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="slavonic" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="日曜大工" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>プーシキン・コンコーダンス・プロトタイプを Web に搭載してみた。<a href="http://www.webtoolkit.eu/wt/" target="_blank">Wt C++ Web Toolkit</a> に基づく C++ Ajax Web アプリケーションである。連休に入って，Wt を勉強しつつプログラムを書いた。Wt の使い方がまだよくわかっておらず，とりあえず動くものができた程度である。</p>

<p><a lang="ru" href="http://yasuda.homeip.net/pushkin/lemmatized/concordance" target="_blank">Динамический Конкорданс к сочинениям А. С. Пушкина</a> からこの Web プロトタイプをお試しいただける。もっともまだ動作をきちんと検証していないし，不安定であるかも知れず，もとより <span lang="ru">«Евгений Онегин»</span> のコーパスしかロードしていない。そのあたりご了承いただきたい。（5.6 付記：その後，プーシキン全集コーパスを搭載し，URL も 8080 ポート番号を使用せずに済むよう変更した）</p>

<p>プロトページにアクセスすると，テキストボックスとボタンがある。テキストボックスに単語式（求める単語の形を表現する条件）を入力し，<span lang="ru">поиск</span> をクリックすると，式に合致する単語について，見出語形式で KWIC コンコーダンス表が生成される。見出語は <a href="http://lemmatizer.org/" target="_blank">Lemmatizer ロシア語形態素解析ライブラリ</a>で機械的に生成したものである。</p>

<p>単語式はそのものズバリの単語を書いてもよいし，メタ文字を組み合わせた正規表現でもよい。メタ文字が含まれないと完全一致探索で単語二分木を探索する。この場合，見出語形式で入力しないと単語がヒットしない。例えば，<span lang="ru" class="redmono">люблю</span> と入力しても，この語の見出語は <span lang="ru" class="redmono">любить</span> なのでヒットしない。<span lang="ru" class="redmono">^люб.*</span> のように正規表現なら，語頭が <span lang="ru" class="redmono">люб</span> の任意の単語 <span lang="ru" class="redmono">любить</span>, <span lang="ru" class="redmono">любоваться</span>,  <span lang="ru" class="redmono">любовница</span> 等々がヒットする。ユーザの入力内容は大文字変換される。case の区別はない。ラテンアルファベット文字はプロトタイプではまだ未調整なので使えない。正規表現の概念については，<a href="http://www.google.co.jp/url?q=http://ja.wikipedia.org/wiki/%25E6%25AD%25A3%25E8%25A6%258F%25E8%25A1%25A8%25E7%258F%25BE&sa=U&ei=5cefT4qAG6vDmQWJpPX5AQ&ved=0CBUQFjAA&sig2=UOZW_6ahdeD9ZfVdAyI0vw&usg=AFQjCNF70FrZ8a7RicIaDJFH5Myq5x2jmw" target="_blank">正規表現 - Wikipedia</a> を参照のこと。</p>

<p>二つの単語式の間に空白を置かず <span class="redmono">&lt;W12&gt;</span> などと書くと，近接隣接演算ができる。<span class="redmono">&lt;W数値&gt;</span>，<span class="redmono">&lt;L数値&gt;</span> は，それぞれ，単語，行について，数値の範囲内で共起する条件を表現している。これは後置式単項演算子であって，<span class="redmono">単語式A&lt;Wn&gt;単語式B</span> は，n 語の範囲内で単語式Bと共起する単語式Aの KWIC を求める動作を規定している。単語式Bでは KWIC を生成しないので注意。指定できる数値の最大は 1000 としてある。ここで「行」は，詩の場合は詩行，散文の場合は概ねパラグラフと考えていただきたい。</p>

<p>探索・KWIC 出力例を図 1. に示す。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120501-concwin.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120501-concwin.png" width="606" height="448" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>図 1. KWIC 出力例</u></div>

<p>これは，<span lang="ru" class="redmono">.*блед.*&lt;W5&gt;татьян.* ^П[ЕЁ]СТР.*</span> という 2 式を指定したものである。空白文字で区切って一度に複数の条件を指定することが可能である。式ごとに <span lang="ru" class="redmono">Выражение: </span> と青地のヘッダが表示され KWIC 表が生成される。一つ目は <span lang="ru" class="redmono">татьян.*</span> に合致する語の 5 語以内に出現する <span lang="ru" class="redmono">.*блед.*</span> 適合語の近接隣接演算探索指定である。二つ目は，語頭が <span lang="ru" class="redmono">П</span> で，<span lang="ru" class="redmono">Е</span> もしくは <span lang="ru" class="redmono">Ё</span> がこれに続き，<span lang="ru" class="redmono">СТР</span> が現われる正規表現探索である。二つの式がそれぞれ 3 件，12 件ヒットしたことがわかる。</p>

<p>KWIC 表はヒットした語を中央に配置し，その前後テクストを含むコンテクストを表示する。赤のボールドイタリックはヒットした語，薄紅背景テクストはヒット行を示す。コーパスの前後行を合わせて 3 行分出力するので，テクストが多いとヒット語を境に複数行になって少し視にくいかも知れない。</p>

<p>KWIC 表の右端には語の位置情報が表示される。例えば <span class="redmono">03:0836:000345</span> という表示は，03 （ジャンル＝韻文小説），0836（作品ID＝<span lang="ru">«Евгений Онегин»</span>『エヴゲーニイ・オネーギン』），345（行）である。ジャンル，作品IDの数値は利用者からすれば何の意味も喚起しないわけだけれども，この位置情報には作品の Web ページ（図 2.）へのリンクが埋め込まれており，ここから当該作品のヒット行に直接ジャンプすることができる。また，リンクにカーソルを合わせると，作品名（作品データベース情報）がポップアップで確認できる（図 3.）。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120501-conceo1.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120501-conceo1.png" width="606" height="679" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>図 2. 作品ページ</u></div>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120501-concpos.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120501-concpos.png" width="588" height="449" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>図 3. 位置情報リンク</u></div>

<p>きちんとした版の公開まではもう少し時間がかかりそうである。すべての作品コーパスをロードし，ジャンルを選択できるようにしなければならない。ロシア語キーボードが使えないユーザのために疑似キーボードも用意しなければならない。</p>]]>
        
    </content>
</entry>

<entry>
    <title>Concordance to Pushkin, prototype</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/04/concordance-to-pushkin-prototype.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1150</id>

    <published>2012-04-28T15:57:56Z</published>
    <updated>2012-04-28T17:45:39Z</updated>

    <summary>連休に入った今日，プーシキン作品コンコーダンス・プログラム試作品に取り組んだ。共...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="slavonic" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="日曜大工" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>連休に入った今日，プーシキン作品コンコーダンス・プログラム試作品に取り組んだ。共有メモリ上にコーパス（全作品テキストデータ），作品名 DB（コーパス ID から作品名を引き当てるデータベース），出現する単語を見出語形式で保持する二分木データベースを構築するのに，おそろしく時間を要したけれども，やっとプロトタイプにこぎ着けた。コーパス，作品名 DB，単語二分木のローダーと，これらのデータを用いてユーザ指定条件に合致する KWIC コンコーダンス表を生成する C++ アプリ。</p>

<p><a lang="ru" href="http://yasuda.homeip.net/concordance/index-r.html" target="_blank">現行版コンコーダンス・プログラム Опыт динамического составления конкордации к текстам А. С. Пушкина</a> に対し，今回の改良点は以下の通り。</p>

<ol>
<li>見出語ベース（UTF-8 ロシア語 Lemmatizer 形態素解析に基づく）</li>
<li>正規表現による柔軟な対象語指定が可能</li>
<li>近接隣接演算のサポート</li>
<li>単語出現位置情報から作品名が参照可能</li>
<li>単語出現位置情報から作品 Web ページの該当部分にリンクが可能</li>
<li>ほぼメモリ上のみでデータ処理を行うので比較的高速</li>
</ol>

<p>近接隣接演算というのは，ある語と何行以内，何語以内の範囲に出現するという条件指定でのコンコーダンス対象語検索である。例えば，「A&lt;W10&gt;B」 という検索式で，語 B と 10 語以内で共起する語 A の KWIC コンコーダンスを生成する。"W" が "L" だと行単位で同じ計算を行う。作品内の語，行の位置情報を単語に付加して管理するので，こうした計算が可能なのである。</p>

<p>プロトタイプなのでまだコンソールアプリである。これを近々 Web で使えるようにしたいと考えている。『エヴゲーニイ・オネーギン』全編のみをコーパスとしてロードし，近接隣接演算を試してみた。<span lang="ru">.*УКА$&lt;W5&gt;.*УКА$</span> という条件。<span lang="ru">.*УКА$</span> マッチ語と5 語範囲内で共起する <span lang="ru">.*УКА$</span> マッチ語について KWIC を生成する指定である。<span lang="ru">.*УКА$</span> という正規表現は，任意の文字ではじまり語末尾が <span lang="ru">УКА</span> である条件に適合する語を示す。</p>

<p>結果は以下の通り。最初の出力は，<span lang="ru" class="redmono">МУКА</span> が 1 件ヒットしたことを示す。コンテキスト中の該当行・単語を強調表示タグ装飾してある。4 語前に <span lang="ru" class="redmono">порукой (見出語 порука)</span> という語があるので，近接隣接条件に合致する。<span class="redmono">03:0836:002129</span> は位置情報（『エヴゲーニイ・オネーギン』の 2129 行目）である。あとに，作品名と，Web ページの該当行へのリンクが続く。</p>

<pre class="brush: plain; gutter: false;">
Expression: .*УКА$&lt;W5&gt;.*УКА$
МУКА    1
1   Поверьте (совесть в том порукой), &lt;span class="hitl"&gt;Супружество 
нам будет &lt;em&gt;мукой&lt;/em&gt;.&lt;/span&gt; Я, сколько ни любил бы вас,
    03:0836:002129  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#002272
НАУКА   3
2   И лучше выдумать не мог. &lt;span class="hitl"&gt;Его пример другим 
&lt;em&gt;наука&lt;/em&gt;;&lt;/span&gt; Но, боже мой, какая скука
    03:0836:000025  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#000022
3   Хоть и ему иные штуки &lt;span class="hitl"&gt;Не проходили без 
&lt;em&gt;науки&lt;/em&gt;,&lt;/span&gt; Хоть иногда и сам впросак
    03:0836:003294  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#003516
4   И не входила глубоко &lt;span class="hitl"&gt;В сердца мятежная 
&lt;em&gt;наука&lt;/em&gt;,&lt;/span&gt; Все это было только скука,
    03:0836:005763  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#006098
ПОРУКА  1
5   Их вовсе недостоин я. &lt;span class="hitl"&gt;Поверьте (совесть в том 
&lt;em&gt;порукой&lt;/em&gt;),&lt;/span&gt; Супружество нам будет мукой.
    03:0836:002128  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#002271
РУКА    4
6   Они сидят в потемках двое; &lt;span class="hitl"&gt;Они в саду, 
&lt;em&gt;рука&lt;/em&gt; с рукой,&lt;/span&gt; Гуляют утренней порой;
    03:0836:002284  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#002438
7   Они сидят в потемках двое; &lt;span class="hitl"&gt;Они в саду, рука с 
&lt;em&gt;рукой&lt;/em&gt;,&lt;/span&gt; Гуляют утренней порой;
    03:0836:002284  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#002438
8   Но кушать подали. Четой &lt;span class="hitl"&gt;Идут за стол 
&lt;em&gt;рука&lt;/em&gt; с рукой.&lt;/span&gt; Теснятся барышни к Татьяне;
    03:0836:003017  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#003221
9   Но кушать подали. Четой &lt;span class="hitl"&gt;Идут за стол рука с 
&lt;em&gt;рукой&lt;/em&gt;.&lt;/span&gt; Теснятся барышни к Татьяне;
    03:0836:003017  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#003221
СКУКА   2
10  Его пример другим наука; &lt;span class="hitl"&gt;Но, боже мой, какая 
&lt;em&gt;скука&lt;/em&gt;&lt;/span&gt; С больным сидеть и день и ночь,
    03:0836:000026  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#000023
11  В сердца мятежная наука, &lt;span class="hitl"&gt;Все это было только 
&lt;em&gt;скука&lt;/em&gt;,&lt;/span&gt; Безделье молодых умов,
    03:0836:005764  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#006099
ШТУКА   1
12  Иль явно, иль исподтишка, &lt;span class="hitl"&gt;Хоть и ему иные 
&lt;em&gt;штуки&lt;/em&gt;&lt;/span&gt; Не проходили без науки,
    03:0836:003293  Роман в стихах. "Евгений Онегин"
    http://yasuda.homeip.net/pushkin/web/0836.html#003515</pre>]]>
        
    </content>
</entry>

<entry>
    <title>Wt C++ Web Toolkit</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/04/wt-cpp-web-toolkit.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1149</id>

    <published>2012-04-25T14:10:01Z</published>
    <updated>2012-04-27T18:07:16Z</updated>

    <summary>通常 Java Servlet で実装する Ajax 非同期通信 Web サーバ...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="日曜大工" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>通常 Java Servlet で実装する Ajax 非同期通信 Web サーバアプリケーションを C++ で書けないか調べていて，<a href="http://www.webtoolkit.eu/wt/" target="_blank">Wt ("Witty", a C++ Web Toolkit)</a> というツールキットの存在を知った。Widget（画面構築部品のようなもの）志向の強い設計になっていて，Web 画面を Widget で組立てることができる。画像操作にも強い。メッセージを言語に応じて切り替える API も備えている。クエリを出す HTML や Java<wbr />Script も Widget で自動的に生成してしまうような凝り様である。</p>

<p>私としては，クエリを出す HTML + JavaScript を別リソースとして用意し，サーバ側だけが Java ではなく C++ のアプリが動いている，というイメージのほうがわかり易いのだけれども，Wt の方式に則れば，いわゆるビジネスロジックのすべてをユーザから見えにくくするというメリットがある（HTML，Java<wbr />Script を独立したリソースとして公開すると，Java<wbr />Script コードから業務データ構造がパクられるなどの危険性がある）。今日は，Mac OS X Snow Leopard への Wt インストール備忘録。</p>

<p>Wt は CMake と Boost C/C++ ライブラリを前提とする。これらをあらかじめ導入しておく。ここではその導入手順は割愛。Wt 最新バージョンは <span class="redmono">3.2.1</span> であり，<a href="http://www.webtoolkit.eu/wt/download" target="_blank">Wt ダウンロードページ</a>から取得できる。私は開発最中の最新 Git リポジトリから引っ張って来てインストールした。ここではそのトップディレクトリ名として <span class="redmono">wt</span> を説明のために使うものとする。<span class="redmono">wt-<wbr />3.<wbr />2.<wbr />1.<wbr />tar.<wbr />gz</span> アーカイブを展開した場合は <span class="redmono">wt-<wbr />3.<wbr />2.<wbr />1</span> となる。</p>

<pre class="brush: term; gutter: false;" title="Git clone">
% cd ~/tmp
% git clone http://www.webtoolkit.eu/git/wt.git</pre>

<p><span class="redmono">wt</span> 直下にある <span class="redmono">INSTALL</span> ファイルに導入手順が書かれている。オプション的に入れておくとよい画像処理ライブラリについてはこちらを参照のこと。以下のようにして Wt リソースをコンパイルする。</p>

<pre class="brush: term; gutter: false;" title="Wt install operation">
% cd ~/tmp/wt
% mkdir build
% cd build
% cmake -DBOOST_DIR=/usr/local ../
% make</pre>

<p>けっこう時間がかかる。ところが，私の Mac OS X 環境では，Boost ライブラリの取り込みでリンケージがことごとく失敗してしまい，<span class="redmono">libwt.<wbr />dylib</span> を生成できず悩んだ。FreeBSD でやってみたらすんなりインストールできた。Mac OS X は私の開発用マシンなので，Wt をぜひとも動かせるようにしておきたい。</p>

<p>こういうとき，CMake で自動生成した Makefile を使っているシステムは，どこで何をやっているのか調査するのに大いに苦労する。リソースを <span class="redmono">grep</span> しまくってリンケージ指定を探したら，<span class="redmono">link.<wbr />txt</span> に記述した内容で実行していることがわかった。これを確認すると，C++ コンパイラへの Boost ライブラリ指示が，<span class="redmono">/opt/<wbr />local/<wbr />lib/<wbr />libboost_<wbr />thread-mt.<wbr />dylib /opt/<wbr />local/<wbr />lib/<wbr />libboost_<wbr />regex-mt.<wbr />dylib ...</span> のような感じで羅列されてあった。<span class="redmono">-mt</span> という余計なものが勝手に付加されていて，これじゃ見つからないはずだと納得した。さらにライブラリの指示なら <span class="redmono">-lライブラリ</span> にしたほうがよいのではないかと思われた。</p>

<p><span class="redmono">link.txt</span> はうんざりするほど数がある。というわけで，配下の <span class="redmono">link.<wbr />txt</span> をすべて探し出して，リンク指定を書き換えるシェルスクリプトを書いた。</p>

<pre class="brush: bash; " title="link.txt rewriting script">
#!/bin/sh
# Web toolkit link option 訂正
for i in `find . -name "link.txt"`
do
    cp $i $i.bak
    cat $i | sed -e \
's/\/opt\/local\/lib\/libboost_thread-[^\.]*\.dylib/-lboost_thread/g' |\
    sed -e \
's/\/opt\/local\/lib\/libboost_random-[^\.]*\.dylib/-lboost_random/g' |\
    sed -e \
's/\/opt\/local\/lib\/libboost_regex-[^\.]*\.dylib/-lboost_regex/g' |\
    sed -e \
's/\/opt\/local\/lib\/libboost_signals-[^\.]*\.dylib/-lboost_signals/g' |\
    sed -e \
's/\/opt\/local\/lib\/libboost_system-[^\.]*\.dylib/-lboost_system/g' |\
    sed -e \
's/\/opt\/local\/lib\/libboost_filesystem-[^\.]*\.dylib/-lboost_filesystem/g' |\
    sed -e \
's/\/opt\/local\/lib\/libboost_date_time-[^\.]*\.dylib/-lboost_date_time/g' \
    &gt; $i.new
    mv $i.new $i
done</pre>

<p>ビンゴでした。これを実行した結果，リンケージはすべてうまく行った。<span class="redmono">sudo make install</span> でインストールする。</p>

<p>例題プログラムもコンパイルしておく。こういうのがシステム仕様の理解・勉強にいちばん役立つ。</p>

<pre class="brush: term; gutter: false; " title="examples compiling">
% cd ~/tmp/wt/build
% make -C examples</pre>

<p>コンパイルが通ったら，動かしてみる。Wt はアプリを FastCGI として動作させることができる一方，<span class="redmono">libwthttp</span> とリンケージすることにより，なんとアプリそのものをスタンドアローンの HTTP/WebSockets サーバ（<span class="redmono">wthttp</span>）として動作させることができる。添付の例題プログラムの一つ <span class="redmono">composer</span>（簡易メールソフト）は wthttp サーバとして動作するようコンパイルされる。以下のオペレーションを行って動かしてみる。wthttp サーバ起動のオプション詳細は<a href="http://www.webtoolkit.eu/wt/doc/reference/html/overview.html#wthttpd" target="_blank">ドキュメント 10.4 Wt httpd (command-line or configuration file) options</a> を参照のこと。ロギングなどもでき，なかなかである。シンプルな分，おそらく高速に動作すると思う。</p>

<pre class="brush: term; gutter: false;" title="run composer">
% cd ~/tmp/wt/examples/composer
% ln -s ../../resources .
% ../../build/examples/composer/composer.wt --docroot . \
  --http-address 0.0.0.0 --http-port 8080</pre>

<p>ブラウザでこのサーバにアクセスしてみた画面は次の通り。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120426-wtex.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120426-wtex.png" width="606" height="479" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><u>composer, Wt example</u></div>

<p>いいものを見つけた。早速メーリングリストにも参加を申し込んだ。私のプーシキン・コンコーダンスのコントローラもこれで行くつもりで，いま API を研究中である。</p>]]>
        
    </content>
</entry>

<entry>
    <title>Boost.Regex ロシア語正規表現</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/04/boost-regex-russian.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1148</id>

    <published>2012-04-23T16:54:50Z</published>
    <updated>2012-04-23T23:38:08Z</updated>

    <summary>プーシキン・コンコーダンス・プログラムの新バージョンでは，検索対象語の指定に正規...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="slavonic" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="日曜大工" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>プーシキン・コンコーダンス・プログラムの新バージョンでは，検索対象語の指定に正規表現を使用できるようにする設計になっている。メタ文字（直前の 0 個以上の文字にマッチする "*" など）を指定した抽象的パターン表現をサポートする。例えば，<span lang="ru">.*ходить</span> という指定に対し，<span lang="ru">приходить や подходить</span> など <span lang="ru">ходить</span> が一致するものすべての語でコンコーダンスを生成するのである。正規表現はプログラミング言語の世界では一般的である。</p>

<p>ロシア語等の Unicode 文字列でも正規表現パターンマッチを支援する C/C++ ライブラリがある。<a href="http://www.boost.org/doc/libs/1_49_0/libs/regex/doc/html/index.html" target="_blank">Boost.<wbr />Regex</a> with ICU である。<a href="http://site.icu-project.org/" target="_blank">ICU（International Components for Unicode）</a> は IBM が開発した Unicode ライブラリである。</p>

<p>Unicode の一般的符号化形式である UTF-8 では，ロシア語・キリル文字は 2 オクテットで表現される（漢字の場合は 3 オクテット以上で符号化される）。ここで通常の正規表現ライブラリ（Unicode データ構造を考慮しない通常の Boost.<wbr />Regex）を用いると，例えば，「任意の一文字」にマッチするメタ文字 "."（ピリオド）について，1 文字が 2 オクテットのキリル文字の検査で不具合が出る。ICU とセットで Boost.<wbr />Regex を用いると，これがきちんと走査できるのである。しかも簡単。私が書いたテスト・コードを以下に示しておく。もちろん，これ以外にもさまざまな機能があるので，<a href="http://www.boost.org/doc/libs/1_49_0/libs/regex/doc/html/index.html" target="_blank">Boost.<wbr />Regex ドキュメント</a>を参照のこと。</p>

<pre class="brush: cpp; " title="regextest.cpp">
/* -*- coding: utf-8; mode: c++; -*-
 * regex 試験
 */
#include &lt;iostream&gt;
#include &lt;boost/regex.hpp&gt;
#include &lt;boost/regex/icu.hpp&gt;
using namespace boost;
 
int main()
{
    smatch m;   // マッチャー
    u32regex r; // 正規表現パターン
    const char* str1 = "Мой дядя самых честных правил";
    const char* str2 = "правил";
 
    // 試験1
    try {
        r = make_u32regex("честн.*");
    }
    catch (const regex_error& e) {
        std::cerr &lt;&lt; "Regex error: " &lt;&lt; e.what() &lt;&lt; std::endl;
    }
    if (u32regex_search(str1, m, r)) {
        std::cout &lt;&lt; "Found (pos=" &lt;&lt; m.position() &lt;&lt; "): " 
                  &lt;&lt; m.str() &lt;&lt; std::endl;
    } else {
        std::cout &lt;&lt; "Not Found." &lt;&lt; std::endl;
    }
 
    // 試験2
    regex_constants::syntax_option_type opt = regex_constants::perl;
    try {
        r = make_u32regex(".*ви.", opt);
    }
    catch (const regex_error& e) {
        std::cerr &lt;&lt; "Regex error: " &lt;&lt; e.what() &lt;&lt; std::endl;
    }
    if (u32regex_match(str2, m, r)) {
        std::cout &lt;&lt; "Matched: " &lt;&lt; str2 &lt;&lt; std::endl;
    } else {
        std::cout &lt;&lt; "Not Matched: " &lt;&lt; str2 &lt;&lt; std::endl;
    }
 
    return 0;
}</pre>

<p><span class="redmono">boost::u32regex boost::make_u32regex("パターン");</span> が正規表現パターンの指定である。"パターン" のところに Unicode 文字を使うことができる。検査したい文字列で <span class="redmono">const char*</span> を初期化し，検査結果を入れるマッチャー <span class="redmono">boost::smatch</span> を用意して，探索 <span class="redmono">boost::u32regex_search()</span> やマッチング <span class="redmono">boost::u32regex_match()</span> などの関数を実行する。対象文字列は <span class="redmono">const char*</span> 以外もサポートしているが，これがいちばん馴染みがある。こちらでマルチバイト文字 <span class="redmono">wchar_t*</span> で用意する必要がなく，簡便である。このあたり，<a href="http://www.boost.org/doc/libs/1_49_0/libs/regex/doc/html/boost_regex/ref/non_std_strings/icu.html" target="_blank">Boost ドキュメント Working With Unicode and ICU String Types</a> を参照。</p>

<p>上記のコードをコンパイルするには以下のようにする。Boost / ICU ライブラリを指定しないとリンケージがうまくいかないので注意。</p>

<pre class="brush: term; gutter: false">
% g++ -L/usr/local/lib -lboost_regex -lboost_system \
-licudata -licui18n -licuuc -g -o regextest regextest.cpp</pre>

<p>実行結果は次のとおり。Unicode 文字がきちんと処理できているのがわかる。</p>

<pre class="brush: term; gutter: false">
% regextest
Found (pos=27): честных правил
Matched: правил</pre>]]>
        
    </content>
</entry>

<entry>
    <title>Shared Memory Word Binary Tree</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/04/shared-memory-word-binary-tree.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1147</id>

    <published>2012-04-19T11:02:23Z</published>
    <updated>2012-04-20T06:27:26Z</updated>

    <summary>Boost/Interprocess を用いて，共有メモリ Shared Mem...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="slavonic" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="日曜大工" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>Boost/Interprocess を用いて，共有メモリ Shared Memory に map を構築し，プロセス間共有を実現する方法について<a href="http://nox-insomniae.ddo.jp/insomnia/2012/03/boost-interprocess-shm-operation.html" target="_blank">記事</a>を書いた。コンコーダンスの実装でもこの方法が使えると思ったが，ちょっと甘かった。コーパスから切り出した単語情報ノードは，単語（キー）とその出現件数，単語の出現位置情報リストから構成される設計としたが，map のぶら下がり情報（キーで引かれるデータ）がこのように複雑なデータ構造をもつ class だと，いろいろ試験的にプログラムを書いて試したところ，うまく共有メモリ上に構築できなかった。おそらく私の Boost の使い方がまずいのだと思うが，Boost の膨大なマニュアルを調べ，試験コードを書いて確かめるのにも疲れた。</p>

<p>そこで Boost/Interprocess が提供している map，list などのコンテナに自作 class を格納するのを諦め，共有メモリ上に単語情報二分木 Binary Tree を自力で構築することにした。<span class="redmono">std::string</span> や <span class="redmono">allocator</span> の管理処理（内部カウンタ管理等）がない分，高速化も期待できる。コーパスから単語を切り出すつど単語をキーとして，ツリーにノードを繋げて行く。キーを比較し一致したら単語出現カウンタをインクリメントし単語出現位置情報を追加する。キーより辞書順で小さければ当該ノードよりも左，大きければ右のノードにアクセスする。これをルートノード（最初に登録したもの）からはじめて，左右ノードがなくなる（ポインタが NULL になる）と新しくノードを生成する。これらを再帰的に（芋蔓式に）繰返し，ツリーを構築する。参照もまったく同様に再帰的に処理を行う。ノードには log N 対数計算量でアクセスでき，データ件数が 10000 倍になっても 3 倍程度しか性能が低下しない。このアルゴリズムは辞書管理や連想配列の常套的方式になっている。例えば「<span lang="ru">Мой дядя самых честных правил, Когда не в шутку занемог</span>」というテクストを入力し，単語二分木を作ると図のようなデータ構造になる（まったく関係ないが，プーシキンの『エヴゲーニイ・オネーギン』のこの書き出しは，二分木として恐ろしく均斉が取れていて — ノードへのアクセス効率が高い — 驚く。もしかすると，プーシキンは文字構成のバランスにも気を配ったのかも知れない）。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120419-bintree.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120419-bintree.png" width="496" height="277" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /><br /><u>二分木データ構造</u></div>

<p>共有メモリ上にこの二分木データ構造を構築するのに，C++ 配置 new 演算子（placement new：指定位置にオブジェクトを生成する）によって，確保した共有メモリ領域にあらゆるデータを明示的に割り付けて行く設計とした（フリーストアから自動的に領域を確保する通常の new 演算子ではダメである）。共有メモリの確保・解放とその先頭アドレスの取得において Boost/<wbr />Interprocess 提供関数を利用する以外は，自力で割り付けて行くのである。データを割り付けるつど，消費したメモリサイズだけアドレスを再計算し，ポインタを更新しなければならない。単語情報ノードは，単語文字列，出現回数カウンタ，及び出現位置情報（ジャンル番号，作品番号，HTML 行 ID，行番号，行先頭からの相対位置，作品先頭からの相対位置）リンクリストからなるものとし，これらを構成するデータオブジェクト，ポインタ（アドレスデータ）すべてを位置指定で共有メモリ領域に格納して行く。</p>

<p>このとき，注意しなければならないのは，<u>共有メモリ内のデータには通常のポインタ（仮想記憶アドレス）を格納できない</u>ということである。文字列データを格納したいとき，その仮想記憶アドレスを指す <span class="redmono">char*</span> ポインタを共有メモリ内に置くのは，不可である。当該プロセスからのみ使う場合はこの制約はない。けれども，構築した二分木を他のプロセスからもアクセスするという本来的目的には使えない。何故なら，<u>共有メモリはバインドされる仮想記憶アドレスがプロセスによって異なる場合がある</u>ため，別のプロセス（自己の仮想記憶のアドレス・ポインタを格納したプロセスとは別のプロセス）がそのポインタの指すアドレスを参照すると，とんちんかんなメモリ領域にすっとんで，記憶保護例外（segmentation fault）で転けたり，無関係のデータを取得したりしてしまうからである。</p>

<p>では，共有メモリ内にポインタを格納して他のプロセスからも共有したいときにどうするかというと，オフセット・ポインタを使うのである。要するに，仮想記憶アドレスではなく，あるアドレスからの相対位置（オフセット）をポインタとして格納し，アクセスする際にプロセス内での実際の仮想記憶アドレスを計算してポイントするのである。Boost/<wbr />Interprocess は，このための便利なポインタ class <span class="redmono">offset_ptr&lt;typename&gt;</span> を提供している。</p>

<p>上記の制約を踏まえ，単語情報二分木・共有メモリ構築サーバプログラム，二分木アクセスクライアントの，二つの簡単なプロトタイプを書いた。前者は，コーパステクストを読んで，Lemmatizer で見出語解析を行い，見出語をキーとした単語情報ノードからなる二分木を共有メモリに構築する。格納するポインタはすべて <span class="redmono">offset_ptr&lt;typename&gt;</span> である。後者は，前者が作成した共有メモリ二分木を参照し，すべてのノード及び，指定した単語の探索結果を印字する。単語情報ノード class 定義やそのメンバ関数（ノード登録，印字，探索ほか）は，次の共通ヘッダファイル <span class="redmono">lemcorpus.h</span> に記述している。</p>

<pre class="brush: cpp;" title="lemcorpus.h">
/* -*- coding: utf-8; mode: c++; -*-
 * Lemmatized Word Tree, Corpus common header 
 * 2012(c) isao yasuda.
 */
 
#ifndef SHARED_MEMORY_CORPUS_WORD_TREE
#define SHARED_MEMORY_CORPUS_WORD_TREE
 
#include &lt;boost/interprocess/managed_shared_memory.hpp&gt;
#include &lt;boost/interprocess/allocators/allocator.hpp&gt;
#include &lt;boost/interprocess/containers/string.hpp&gt;
#include &lt;boost/interprocess/containers/vector.hpp&gt;
#include &lt;boost/interprocess/offset_ptr.hpp&gt;
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;sstream&gt;
#include &lt;csignal&gt;
 
// Typedefs of allocators and containers
using namespace boost::interprocess;
typedef managed_shared_memory::segment_manager   segment_manager_t;
typedef allocator&lt;char, segment_manager_t&gt;       char_allocator;
typedef basic_string&lt;char, std::char_traits&lt;char&gt;, char_allocator&gt; char_string;
typedef allocator&lt;char_string, segment_manager_t&gt; string_allocator;
typedef vector&lt;char_string, string_allocator&gt;    shm_vector;
 
// shared memory constants
static const char* SHMTREE = "ShmWordTree";    // 共有メモリ名 Word Tree
static const char* SHMCRPS = "ShmCorpus";      // 共有メモリ名 Corpus
static const int   SHMTRSZ = 1024 * 1024 * 10; // 共有メモリサイズ Word Tree
static const int   SHMCRSZ = 1024 * 1024 * 10; // 共有メモリサイズ Corpus
static const int   MNGSIZE = 256;              // 管理領域(使用不可エリア)サイズ
static char* sp; // 共有メモリ内アロケーションポインタ
 
// 位置情報クラス
class wpos {
    int genre;             // ジャンル
    int fname;             // ファイル名
    int linid;             // 行 id
    int linno;             // 行通番
    int linps;             // 行内出現アドレス(行先頭からの相対位置)
    int wdpos;             // 単語出現アドレス(作品先頭からの相対位置)
    offset_ptr&lt;wpos&gt; next; // 次の位置情報への相対ポインタ
    friend class tree;
public:
    wpos(int ge, int fn, int id, int ln, int lp, int wp)
        : genre(ge), fname(fn), linid(id), linno(ln), linps(lp), wdpos(wp), 
          next(NULL)
    {}
    void printwpos(); // 位置情報出力
};
 
// 位置情報出力: 全位置情報を芋蔓式に出力する
void wpos::printwpos() {
    if (this != NULL) {
        std::cout &lt;&lt; genre &lt;&lt; ":" &lt;&lt; fname &lt;&lt; ":" &lt;&lt; linid &lt;&lt; ":"
                  &lt;&lt; linno &lt;&lt; ":" &lt;&lt; linps &lt;&lt; ":" &lt;&lt; wdpos &lt;&lt; "; ";
        next-&gt;wpos::printwpos();
    } else {
        std::cout &lt;&lt; "\n";
    }
}
 
// 二分木クラス
class tree {
private:
    // 単語ノードクラス
    class node {
        offset_ptr&lt;char&gt; word;  // 単語への相対ポインタ
        int   counter;          // 出現回数
        offset_ptr&lt;wpos&gt; fpos;  // 最初の位置情報への相対ポインタ
        offset_ptr&lt;wpos&gt; lpos;  // 最後の位置情報への相対ポインタ
        offset_ptr&lt;node&gt; right; // 右ノードへの相対ポインタ
        offset_ptr&lt;node&gt; left;  // 左ノードへの相対ポインタ
        friend class tree;
    };
    // ルート
    offset_ptr&lt;node&gt; root;
    // ノード登録
    void enter_node(offset_ptr&lt;node&gt;& node, const std::string& word,
                    int gi, int fi, int ii, int li, int pi, int wi);
    // ノード出力
    void print_node(offset_ptr&lt;node&gt;& top);
    // ノード検索
    void find_node(const std::string& word, offset_ptr&lt;node&gt;& top);
public:
    tree() { root = NULL; }
    // ツリー登録
    void enter_tree(const std::string& new_word,
                    int gi, int fi, int ii, int li, int pi, int wi) {
        enter_node(root, new_word, gi, fi, ii, li, pi, wi);
    }
    // ツリー出力
    void print_tree() {
        print_node(root);
    }
    // ツリー検索
    void find_tree(const std::string& word) {
        find_node(word, root);
    }
};
 
// ノード登録:
void tree::enter_node(offset_ptr&lt;node&gt;& new_node, const std::string& new_word,
                      int gi, int fi, int ii, int li, int pi, int wi)
{
    // 登録単語サイズ
    size_t sz = new_word.size() + 1;
 
    // ノードポインタが NULL の場合: 新規登録
    if (new_node == NULL) {
        // 新しい node を生成
        new_node = new(reinterpret_cast&lt;node*&gt;(sp)) node;
        sp += sizeof(node); // ポインタ更新
        // 左右ポインタ初期化
        new_node-&gt;left  = NULL;
        new_node-&gt;right = NULL;
        // 単語登録
        new_node-&gt;word = new(sp) char[sz]; 
        std::strcpy((char*) sp, new_word.c_str());
        sp += sz; // ポインタ更新
        // カウンタ初期化
        new_node-&gt;counter = 1;
        // wpos 初期登録
        new_node-&gt;fpos = new(reinterpret_cast&lt;wpos*&gt;(sp)) 
            wpos(gi, fi, ii, li, pi, wi);
        new_node-&gt;lpos = new_node-&gt;fpos;
        sp += sizeof(wpos); // ポインタ更新
        return;
    }
 
    // offset_ptr を char* に変換
    char* wp = (new_node-&gt;word).get();
 
    // 既登録の単語と一致した場合: カウンタをアップし，位置情報を追加
    if (std::strcmp(wp, new_word.c_str()) == 0) {
        new_node-&gt;counter += 1;
        (new_node-&gt;lpos)-&gt;next = new(reinterpret_cast&lt;wpos*&gt;(sp)) 
            wpos(gi, fi, ii, li, pi, wi);
        new_node-&gt;lpos = (new_node-&gt;lpos)-&gt;next;
        sp += sizeof(wpos); // ポインタ更新
        return;
    }
 
    // 既登録単語より辞書順で後なら右ノードへ，前なら左ノードへ 
    if (std::strcmp(wp, new_word.c_str()) &lt; 0)
        enter_node(new_node-&gt;right, new_word, gi, fi, ii, li, pi, wi);
    else
        enter_node(new_node-&gt;left, new_word, gi, fi, ii, li, pi, wi);
}
 
// ノード出力
void tree::print_node(offset_ptr&lt;node&gt;& top)
{
    // ノードポインタが NULL ならそこで終了
    if (top == NULL) return;
 
    // 左ノードを出力
    print_node(top-&gt;left);
 
    // 現ノードを出力
    char* wp = (top-&gt;word).get(); // offset_ptr を char* に変換
    std::cout &lt;&lt; wp &lt;&lt; " " &lt;&lt; top-&gt;counter &lt;&lt; " ";
    (top-&gt;fpos)-&gt;printwpos();
 
    // 右ノードを出力
    print_node(top-&gt;right);
}
 
// ノード検索
void tree::find_node(const std::string& tword, offset_ptr&lt;node&gt;& tnode)
{
    // ノードポインタが NULL ならそこで終了
    if (tnode == NULL) return;
 
    // offset_ptr を char* に変換
    char* wp = (tnode-&gt;word).get();
 
    // 指定単語に一致したら現ノードを出力
    if (std::strcmp(wp, tword.c_str()) == 0) {
        std::cout &lt;&lt; wp &lt;&lt; " " &lt;&lt; tnode-&gt;counter &lt;&lt; " ";
        (tnode-&gt;fpos)-&gt;printwpos();
        return;
    }
 
    // 既登録単語より辞書順で後なら右ノード，前なら左ノードを検索 
    if (std::strcmp(wp, tword.c_str()) &lt; 0)
        find_node(tword, tnode-&gt;right);
    else
        find_node(tword, tnode-&gt;left);
}
 
// シグナル・ハンドラ
void term(int n) {
    std::cout &lt;&lt; "Signal caught " &lt;&lt; n &lt;&lt; "\n";
    exit(1);
}
 
// 共有メモリを開始前と終了後に削除する remover
struct shm_wtree_remove
{
    shm_wtree_remove() {
        shared_memory_object::remove(SHMTREE);
    }
    ~shm_wtree_remove(){
        std::cout &lt;&lt; "Word Tree Shared Memory Deconstruction.\n";
        shared_memory_object::remove(SHMTREE);
    }
};
 
struct shm_corpus_remove
{
    shm_corpus_remove() {
        shared_memory_object::remove(SHMCRPS);
    }
    ~shm_corpus_remove(){
        std::cout &lt;&lt; "Corpus Shared Memory Deconstruction.\n";
        shared_memory_object::remove(SHMCRPS);
    }
};
 
#endif</pre>

<p>次は，単語二分木共有メモリ構築プログラム。ロシア語の見出語解析には Lemmatizer を，単語切り出しには Boost/<wbr />tokenizer を使っている。</p>

<pre class="brush: cpp;" title="lemwtreesrv.cpp">
/* -*- coding: utf-8; mode: c++; -*-
 * lemwtreesrv.cpp - Lemmatized Word Tree サーバ
 * - Corpus ファイルを読む
 * - 位置情報と作品テクストを分離する
 * - テクストから単語を切り出し，Lemmatizer で見出語に変換し，単語二分木を構築する
 * - 二分木ノードに単語，出現回数，位置情報リストを付加する
 * 2012(c) isao yasuda.
 */
 
#include &lt;turglem/lemmatizer.hpp&gt;
#include &lt;turglem/russian/charset_adapters.hpp&gt;
#include &lt;boost/tokenizer.hpp&gt;
#include "lemcorpus.h"
using namespace boost::interprocess;
 
// グローバル変数
static offset_ptr&lt;tree&gt; words; // 単語ツリーへのオフセットポインタ
static int wordp = 0;          // 単語アドレス(作品先頭からの相対位置)
static int flid  = 0;          // 現作品番号(ファイル名でもある)
 
// Lemmatizer Russian Grammatical Resources
static char dict[] = "/usr/local/share/turglem/russian/dict_russian.auto";
static char prdm[] = "/usr/local/share/turglem/russian/paradigms_russian.bin";
static char pred[] = "/usr/local/share/turglem/russian/prediction_russian.auto";
static tl::lemmatizer lem;     // lemmatizer instance
static tl::lem_result lar;     // lemmatizer result
 
// Lemmatizer 見出語属性構造体
struct wordst {
    std::string word; // 見出語
    int part;         // 品詞
    int prio;         // プライオリティ
    int len;          // 長さ
};
 
// ロシア語形態素解析 Lemmatizer 見出語選定
void lemmatizer(std::string& tword)
{
    // Lemmatizer 分析結果オブジェクト件数
    size_t rcnt = lem.lemmatize&lt;russian_utf8_adapter&gt;(tword.c_str(), lar);
 
    wordst wst;                        // 見出語属性
    std::vector&lt;wordst&gt; wv;            // 見出語属性 vector
    std::vector&lt;wordst&gt;::iterator wit; // イテレータ
 
    // rcnt が 0 (解析失敗) なら即終了
    if (rcnt &lt; 1) return;
 
    // 見出し語候補 vector を生成し，最大プライオリティを確定
    int maxprio = 0;
    for (size_t i = 0; i &lt; rcnt; i++) {
        u_int32_t src_form = lem.get_src_form(lar, i);
        wst.word = lem.get_text&lt;russian_utf8_adapter&gt;(lar, i, 0);
        wst.part = lem.get_part_of_speech(lar, i, src_form);
        wst.len  = wst.word.size();
        // 品詞番号でプライオリティ付け
        switch (wst.part) {
        case 0:  wst.prio = 5; break; // существительное 名詞
        case 1:  wst.prio = 4; break; // прилагательное 形容詞長語尾形
        case 2:  wst.prio = 3; break; // глагол 動詞
        case 3:  wst.prio = 5; break; // местоимение 代名詞
        case 4:  wst.prio = 4; break; // местоимение 代名詞(関係詞など?)
        case 5:  wst.prio = 4; break; // местоимение 代名詞?
        case 6:  wst.prio = 4; break; // числительное 数詞
        case 7:  wst.prio = 4; break; // порядковое числительное 順序数詞
        case 8:  wst.prio = 3; break; // наречие 副詞
        case 9:  wst.prio = 3; break; // предикатив 述語／副詞?
        case 10: wst.prio = 1; break; // предлог 前置詞
        case 11: wst.prio = 1; break; // POSL 後置詞?
        case 12: wst.prio = 1; break; // союз 接続詞
        case 13: wst.prio = 1; break; // междометие 間投詞
        case 14: wst.prio = 1; break; // (INP ?)
        case 15: wst.prio = 1; break; // (PHRASE 成句?)
        case 16: wst.prio = 1; break; // частица 小詞
        case 17: wst.prio = 3; break; // краткое прилагательное 形容詞短語尾形
        case 18: wst.prio = 2; break; // причастие 形動詞
        case 19: wst.prio = 2; break; // деепричастие 副動詞
        case 20: wst.prio = 2; break; // краткое причастие 形動詞短語尾形
        case 21: wst.prio = 3; break; // инфинитив 動詞不定形
        defaut:  wst.prio = 1; break;
        }
        if (wst.prio &gt;= maxprio)
            maxprio = wst.prio;
        wv.push_back(wst);
    }
 
    // 最大プライオリティ品詞をもつ候補語のなかで最小長のものを確定
    int minlen = 1000;
    std::vector&lt;std::string&gt; vcand;
    for (wit = wv.begin(); wit &lt; wv.end(); wit++) {
        if ((*wit).prio == maxprio) {
            if ((*wit).len &lt;= minlen) {
                minlen = (*wit).len;
                vcand.push_back((*wit).word);
            }
        }
    }
 
    // 最小長の候補語の最初のものを見出語に決定
    std::vector&lt;std::string&gt;::iterator sit;
    for (sit = vcand.begin(); sit != vcand.end(); sit++) {
        if ((*sit).size() ==  minlen) {
            tword = *sit;
            break;
        }
    }
}
  
// コーパス・トークナイザ: corpus 行を単語分割して，vector に格納
// 8 bit の区切り文字 «, » などには未対応 (corpus 作成プログラムで削除しておく)
void corpus_tokenizer(std::string& line, std::vector&lt;std::string&gt;& sv)
{
    typedef boost::tokenizer&lt;boost::char_separator&lt;char&gt; &gt; tokenizer;
    // 区切り文字定義: -' では分割しない
    boost::char_separator&lt;char&gt; sep("\t _:;.,?!~`\"\\[]{}()*&^%$#@+=|&lt;&gt;/", 
                                    "", boost::drop_empty_tokens);
    tokenizer tok(line, sep);
    for(tokenizer::iterator ti = tok.begin(); ti != tok.end(); ti++) 
        sv.push_back(*ti);
}
 
// corpus 行をスキャンし，単語をツリーに登録
void corpus_scan(std::string& cl)
{
    std::vector&lt;std::string&gt; wordv; // 単語 vector
    int posd[4];                    // 位置情報
    int lwp = 0;                    // 行内単語アドレス
 
    // corpus line を語分割して単語 vector に格納
    corpus_tokenizer(cl, wordv);
 
    // 位置情報，corpus 分離
    int i = 0;
    for (std::vector&lt;std::string&gt;::iterator it = wordv.begin();
         it != wordv.end(); it++) {
        if (i &lt; 4)
            // 先頭4語(数字)は，位置情報として int 配列に格納
            posd[i] = atoi((*it).c_str());
        else {
            // Lemmatizer を使用して見出語に変換
            try {
                lemmatizer(*it);
            }
            catch (const std::exception& e) {
                std::cerr &lt;&lt; "Lemmatizer error: " &lt;&lt; e.what() &lt;&lt; "\n";
            }
        }
        i++;
    }
 
    // 現作品番号がキーブレークしたら，作品番号を更新し，単語アドレスをリセット(Global)
    if (posd[1] != flid) {
        flid = posd[1]; // 作品番号
        wordp = 0;      // 単語アドレス(作品先頭からの相対位置)
    }
 
    // 単語 vector の単語，位置情報，単語アドレスを二分木に登録
    for (int i = 4; i &lt; wordv.size(); i++)
        words-&gt;enter_tree
            (wordv[i], posd[0], posd[1], posd[2], posd[3], lwp++, wordp++);
}
 
// 単語情報二分木共有メモリ構築・主処理
int main(int argc, char *argv[])
{
    // 引数チェック
    if (argc != 2) {
        std::cerr &lt;&lt;  "Usage: " &lt;&lt; argv[0] &lt;&lt; " corpus-file\n";
        exit(8);
    }
 
    // 捕捉シグナルとハンドラの登録
    signal(SIGTERM, term);
    signal(SIGINT,  term);
    signal(SIGHUP,  term);
 
    // 共有メモリを開始前と終了後に削除する
    shm_wtree_remove remover;
 
    // 共有メモリに単語二分木を構築 
    std::cout &lt;&lt; "Word Tree Shared Memory Construction.\n";
    managed_shared_memory segment(create_only, SHMTREE, SHMTRSZ);
    void* pt = segment.allocate(SHMTRSZ - MNGSIZE);
 
    // 使用可能共有メモリの先頭アドレスをポイント
    sp = static_cast&lt;char*&gt;(pt);
    char* csp = sp;
 
    // 共有メモリに単語二分木を割当
    words = new(reinterpret_cast&lt;tree*&gt;(sp)) tree;
    sp += sizeof(tree);
 
    // Lemmatizer load dict, paradigms, prediction.
    try {
        lem.load_lemmatizer(dict, prdm, pred);
    }
    catch (const std::exception& e) {
        std::cerr &lt;&lt; "Lemmatizer error: " &lt;&lt; e.what() &lt;&lt; "\n";
    }
 
    // 共有メモリ上に単語二分木を構築 
    std::ifstream ifs(argv[1]);
    std::string ldata;
    while (ifs && getline(ifs, ldata)) {
        corpus_scan(ldata);
    }
 
    // 単語二分木の共有メモリアドレスを連絡するためのハンドルを取得
    std::cout &lt;&lt; "srv word tree address: " &lt;&lt; (void*) csp &lt;&lt; "\n";
    void* p = static_cast&lt;void*&gt;(csp);
    managed_shared_memory::handle_t handle = 
        segment.get_handle_from_address(p);
    std::cout &lt;&lt; "srv word tree handle:  " &lt;&lt; handle &lt;&lt; "\n";
  
    // 何か入力されるまでウェイト
    while (true) {
        std::cout &lt;&lt; "Wait. Signal (TERM, INT, HUP) or any key to exit.\n";
        char x; std::cin &gt;&gt; x; break;
    }
 
    segment.deallocate(pt);
    return (0);
}</pre>

<p>最後に，他プロセスから共有メモリを参照するクライアントプログラム。</p>

<pre class="brush: cpp;" title="lemwtreesrv.cpp">
/* -*- coding: utf-8; mode: c++; -*-
 * lemwtreeclt.cpp - Lemmatized Word Tree クライアント
 * - 共有メモリ Word 二分木アドレスを引数のハンドルから取得する
 * - 二分木のすべてのノードを出力する
 * 2012(c) isao yasuda.
 */
 
#include "lemcorpus.h"
using namespace boost::interprocess;
 
int main (int argc, char* argv[])
{
    // 引数チェック
    if (argc != 2) {
        std::cerr &lt;&lt;  "Usage: " &lt;&lt; argv[0] &lt;&lt; " handle\n";
        exit(8);
    }
 
    // 共有メモリ・オブジェクトを取得
    managed_shared_memory segment(open_only, SHMTREE);
  
    // ハンドルから共有メモリ・リソースのアドレスを取得
    managed_shared_memory::handle_t handle = 0;
    handle = static_cast&lt;managed_shared_memory::handle_t&gt;(std::atoi(argv[1]));
    void* vp = segment.get_address_from_handle(handle);
    std::cerr &lt;&lt; "clt word tree address: " &lt;&lt; vp &lt;&lt; "\n";
 
    // void ポインタを tree ポインタにキャスト
    offset_ptr&lt;tree&gt; sv = reinterpret_cast&lt;tree*&gt;(vp); 
 
    // tree の全ノードを出力
    sv-&gt;print_tree();
 
    // node 探索
    std::string fw = "НЕ";
    std::cout &lt;&lt; "find " &lt;&lt; fw &lt;&lt; "\n";
    sv-&gt;find_tree(fw);
 
    return 0;
}</pre>

<p>上記プログラムを以下の『エヴゲーニイ・オネーギン』からのコーパスデータ・サンプルで実行してみる。行の先頭にあるのは，別途コーパス作成プログラムで付加した管理情報（ジャンル番号，作品番号 = HTML ファイル名，HTML 行 ID，行番号）である。</p>

<pre class="brush: plain; gutter: false;" title="corpus-eo.txt" lang="ru">03:0837:0101001:000021  Мой дядя самых честных правил,
03:0837:0101002:000022 Когда не в шутку занемог,
03:0837:0101003:000023 Он уважать себя заставил
03:0837:0101004:000024 И лучше выдумать не мог.
03:0837:0101005:000025 Его пример другим наука;
03:0837:0101006:000026 Но, боже мой, какая скука
03:0837:0101007:000027 С больным сидеть и день и ночь,
03:0837:0101008:000028 Не отходя ни шагу прочь!
03:0837:0101009:000029 Какое низкое коварство
03:0837:0101010:000030 Полуживого забавлять,
03:0837:0101011:000031 Ему подушки поправлять,
03:0837:0101012:000032 Печально подносить лекарство,
03:0837:0101013:000033 Вздыхать и думать про себя
03:0837:0101014:000034 Когда же черт возьмет тебя!</pre>

<p>二分木構築サーバ，アクセスクライアントの実行結果は，それぞれ以下のとおり。</p>

<pre class="brush: plain; gutter: false;" title="lemwtreesrv" lang="ru">
% lemwtreesrv corpus-eo.txt
Word Tree Shared Memory Construction.
srv word tree address: 0x1010000c0
srv word tree handle:  192
Wait. Signal (TERM, INT, HUP) or any key to exit.
q
Word Tree Shared Memory Deconstruction.
%</pre>

<p>次に示す，Mac OS X でのアクセスクライアント実行結果から，他のプロセスからもきちんと共有メモリデータが見えることがわかる。サーバ側では二分木共有メモリアドレスは <span class="redmono">0x1010000c0</span> であるのに対し，クライアント側では <span class="redmono">0x1002000c0</span> になっている。これは OS の仮想記憶管理に依存するが，このようにそれぞれのプロセスにおいて単一の共有メモリ領域がプロセス毎に違う仮想記憶アドレスにマッピングされる場合がある。だから共有メモリ内のポインタに仮想記憶アドレスそのものを記憶させるとまずいのである。ちなみに，FreeBSD で実行した結果では，二分木アドレスは，サーバ，クライアント両側ともに <span class="redmono">0x48400060</span> であり，同じだった。</p>

<pre class="brush: plain; gutter: false;" title="lemwtreeclt" lang="ru">% lemwtreeclt 192
clt word tree address: 0x1002000c0
БОГ 1 3:837:101006:26:1:24; 
БОЛЬНОЙ 1 3:837:101007:27:1:29; 
В 1 3:837:101002:22:2:7; 
ВЗДЫХАТЬ 1 3:837:101013:33:0:51; 
ВЗЯТЬ 1 3:837:101014:34:3:59; 
ВЫДУМАТЬ 1 3:837:101004:24:2:16; 
ДЕНЬ 1 3:837:101007:27:4:32; 
ДРУГОЙ 1 3:837:101005:25:2:21; 
ДУМАТЬ 1 3:837:101013:33:2:53; 
ДЯДЯ 1 3:837:101001:21:1:1; 
ЖЕ 1 3:837:101014:34:1:57; 
ЗАБАВЛЯТЬ 1 3:837:101010:30:1:44; 
ЗАНЕМОЧЬ 1 3:837:101002:22:4:9; 
ЗАСТАВИТЬ 1 3:837:101003:23:3:13; 
И 4 3:837:101004:24:0:14; 3:837:101007:27:3:31; 3:837:101007:27:5:33; 
 3:837:101013:33:1:52; 
КАКОЙ 2 3:837:101006:26:3:26; 3:837:101009:29:0:40; 
КОВАРСТВО 1 3:837:101009:29:2:42; 
КОГДА 2 3:837:101002:22:0:5; 3:837:101014:34:0:56; 
ЛЕКАРСТВО 1 3:837:101012:32:2:50; 
МОЙ 2 3:837:101001:21:0:0; 3:837:101006:26:2:25; 
МОЧЬ 1 3:837:101004:24:4:18; 
НАУКА 1 3:837:101005:25:3:22; 
НЕ 3 3:837:101002:22:1:6; 3:837:101004:24:3:17; 3:837:101008:28:0:35; 
НИ 1 3:837:101008:28:2:37; 
НИЗКИЙ 1 3:837:101009:29:1:41; 
НО 1 3:837:101006:26:0:23; 
НОЧЬ 1 3:837:101007:27:6:34; 
ОН 3 3:837:101003:23:0:10; 3:837:101005:25:0:19; 3:837:101011:31:0:45; 
ОТХОДИТЬ 1 3:837:101008:28:1:36; 
ПЕЧАЛЬНЫЙ 1 3:837:101012:32:0:48; 
ПОДНОСИТЬ 1 3:837:101012:32:1:49; 
ПОДУШКА 1 3:837:101011:31:1:46; 
ПОЛУЖИВОЙ 1 3:837:101010:30:0:43; 
ПОПРАВЛЯТЬ 1 3:837:101011:31:2:47; 
ПРАВИЛО 1 3:837:101001:21:4:4; 
ПРИМЕР 1 3:837:101005:25:1:20; 
ПРО 1 3:837:101013:33:3:54; 
ПРОЧЬ 1 3:837:101008:28:4:39; 
С 1 3:837:101007:27:0:28; 
САМЫЙ 1 3:837:101001:21:2:2; 
СЕБЯ 2 3:837:101003:23:2:12; 3:837:101013:33:4:55; 
СИДЕТЬ 1 3:837:101007:27:2:30; 
СКУКА 1 3:837:101006:26:4:27; 
ТЫ 1 3:837:101014:34:4:60; 
УВАЖАТЬ 1 3:837:101003:23:1:11; 
ХОРОШИЙ 1 3:837:101004:24:1:15; 
ЧЕРТ 1 3:837:101014:34:2:58; 
ЧЕСТНЫЙ 1 3:837:101001:21:3:3; 
ШАГ 1 3:837:101008:28:3:38; 
ШУТКА 1 3:837:101002:22:3:8; 
find НЕ
НЕ 3 3:837:101002:22:1:6; 3:837:101004:24:3:17; 3:837:101008:28:0:35; 
%</pre>

<p>出力の意味は，「見出語 出現回数 ジャンル番号:作品番号:行ID:行番号:行先頭からの相対位置:作品先頭からの相対位置（複数あり）」である。例えば，<span class="redmono" lang="ru">КАКОЙ 2 3:837:101006:26:3:26; 3:837:101009:29:0:40;</span> というのは，単語 <span class="redmono" lang="ru">КАКОЙ</span> が処理コーパスにおいて，26:3:26 = 26 行目・行語位置 3（行先頭から 4 語目）・作品語位置 26（作品先頭から 27 語目），29:0:40（同左）の 2 回出現していることを示す。「作品番号」と「行ID」は，作品コーパスに対応する HTML のファイル名とその行の ID 要素とに対応しており，この出力結果からコーパス・コンテキストを HTML の当該場所へのリンクによって確認できる設計になっている（次のプロトタイプ作成課題はこれ）。</p>]]>
        
    </content>
</entry>

<entry>
    <title>Russian Word Lemmatizer</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/04/russian-word-lemmatizer-prototyping.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1146</id>

    <published>2012-04-18T13:44:04Z</published>
    <updated>2012-05-06T07:45:40Z</updated>

    <summary>プーシキン作品コンコーダンス・プログラムの設計において悩ましいのが，ロシア単語を...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="UNIX" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="日曜大工" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>プーシキン作品コンコーダンス・プログラムの設計において悩ましいのが，ロシア単語を出現形と見出語形（Lemmatized form）のいずれで扱うべきか，という課題である。自動コンコーダンスの目的は指定した語条件でコーパスを検索し，その用例が一覧できることにある — 私自身はそう思っているので，出現形でも見出語形でもどちらでもよいのだが，世の研究者は見出語にこだわるようで，北大の浦井教授・安藤教授が出版したドストエフスキイとカラムジンのコンコーダンスは見出語で整理されている。例えば，<span lang="ru">говорю</span>（「語る」という動詞一人称現在形）という語（出現形）がテクストに出て来たら <span lang="ru">говорить</span>（その不定形＝見出語）として用例を纏めるのである。というわけで，私の新しいソフトウェアも見出語でコンコーダンスを自動生成できるようにしたいと考えた（現行版 <a lang="ru" href="http://yasuda.homeip.net/concordance/index-r.html" target="_blank">Опыт динамического составления конкордации<br />
к текстам А. С. Пушкина</a> は出現形でしか処理できない）。</p>

<p>そのために<a href="http://lemmatizer.org/" target="_blank">ロシア語形態素解析 C/C++ ライブラリ Lemmatizer</a> を利用することにした。これは UTF-8 エンコードのロシア語テクストを与えると，形態素辞書に基づいて見出語を解析することができる。<a href="http://nox-insomniae.ddo.jp/insomnia/2009/07/lemmatizer-utf8.html" target="_blank">ずいぶん前に Lemmatizer 試験プログラムを書いた</a>ので，見出語選定プロトタイプを作るのは簡単だった。しかしながら，Lemmatizer を用いて出現形を見出語に機械変換するに際して，出現形に対して可能性のある見出語が複数ある場合，予想だにしない結果を招くことがある。</p>

<p>複数のうちのどれを選ぶかを考えず，単純に Lemmatizer の解析結果の最初のものを見出語として採用するとする。例えば <span lang="ru">какая</span>（「どんな」意の疑問代名詞女性単数主格）が出て来たら，普通なら誰でも，見出語は <span lang="ru">какой</span>（男性単数主格）が得られると期待するだろう。ところが，Lemmatizer にかけると，<span lang="ru">какать</span> と <span lang="ru">какой</span> の二つが見出語候補として解析され，最初のものを採用すると，見出語は <span lang="ru">какать</span>（幼児語「ウンチする」の動詞不定形）となってしまうのである。<span lang="ru">какая</span> は確かに <span  lang="ru">какать</span> の副動詞と同形なので「間違い」ではない。でも人間が見たら一瞬でおかしいと思うわけだ。こういう形態上の曖昧さはどうしても自然言語処理には付いて回り，電子コンコーダンスを見出語ベースにする究極の問題点でもある。</p>

<p>周知の通り，ロシア語は名詞・形容詞の格変化，動詞の時制変化・形容詞／副詞化（能動形容詞，被動形容詞，副動詞の変化）など語形変化が凄まじい言語である。機械的に見出語を求めようとすると，上記のような滑稽な判断をする可能性がある。ならどうするか。究極は完璧な見出語選定は不可能であるが，品詞属性に見出語選定プライオリティを付けることによって，少しは改善できるだろうと考えた。<span lang="ru">какая</span> の例では，Lemmatizer 解析で同時に得られる品詞情報に基づいて，動詞よりも代名詞のほうを優先して採用するようにすればよい。</p>

<p>「名詞・代名詞 &gt; 形容詞長語尾形・物主他代名詞・数詞 &gt; 副詞／述語・動詞人称／時制変化／不定形・形容詞短語尾形 &gt; 能動／被動形動詞・副動詞 &gt; 前置詞・接続詞・間投詞・小詞」の順で品詞優先度を付け，複数の解析結果がある場合，優先度の高いほうを見出語として採用することにした。同じ優先度のものが複数ある場合は，語長の短いもの，さらに優先度／語長の同じものが複数ある場合は最初に返却されたもの，とすることにした。</p>

<p>見出語選定プロトタイプ・プログラム <span class="redmono">lemmatizer.cpp</span> を以下に示す。行テクストから句読点・約物を取り除いて露単語を切り出すのに <span class="redmono">Boost/<wbr />tokenizer</span> を使用した。</p>

<pre class="brush: cpp; " title="lemmatizer.cpp">
/* -*- coding: utf-8; mode: c++; -*-
 *
 *  Lemmatizer 解析／見出語選定
 *
 *    見出し語候補から以下の条件でひとつを採用する
 *    - 品詞プライオリティが最も高い
 *    - 単語長が最も短い
 *    - そのなかで Lemmatizer から最初に返却されるもの
 *
 *                            2012 (c) isao yasuda.
 */
 
#include &lt;turglem/lemmatizer.hpp&gt;
#include &lt;turglem/russian/charset_adapters.hpp&gt;
#include &lt;boost/tokenizer.hpp&gt;
#include &lt;iostream&gt;
#include &lt;fstream&gt;
 
// Lemmatizer ロシア語形態素解析辞書
static char dict[] =
    "/usr/local/share/turglem/russian/dict_russian.auto";
static char prdm[] =
    "/usr/local/share/turglem/russian/paradigms_russian.bin";
static char pred[] = 
    "/usr/local/share/turglem/russian/prediction_russian.auto";
 
// Lemmatizer 見出語属性構造体
struct wordst {
    std::string word; // 見出語
    int part;         // 品詞
    int prio;         // プライオリティ
    int len;          // 長さ
};
 
// Lemmatizer 解析／見出語選定
void lem_analyze(const tl::lemmatizer& lem, const char* s)
{
    // Lemmatizer 分析結果オブジェクトと件数
    tl::lem_result lr;
    size_t rcnt = lem.lemmatize&lt;russian_utf8_adapter&gt;(s, lr);
 
    wordst wst;                        // 見出語属性
    std::vector&lt;wordst&gt; wv;            // 見出語属性 vector
    std::vector&lt;wordst&gt;::iterator wit; // イテレータ
 
    // 見出し語候補 vector を生成し，最大プライオリティを確定
    int maxprio = 0;
    for (size_t i = 0; i &lt; rcnt; i++) {
        u_int32_t src_form = lem.get_src_form(lr, i);
        wst.word = lem.get_text&lt;russian_utf8_adapter&gt;(lr, i, 0);
        wst.part = lem.get_part_of_speech(lr, i, src_form);
        wst.len  = wst.word.size();
        std::cout &lt;&lt; "   [" &lt;&lt; i &lt;&lt; "] " &lt;&lt; wst.word 
                  &lt;&lt; "\t品詞番号: " &lt;&lt; wst.part &lt;&lt; std::endl;
        // 品詞番号でプライオリティ付け
        switch (wst.part) {
        // 品詞番号: プライオリティ
        case 0:  wst.prio = 5; break; // существительное 名詞
        case 1:  wst.prio = 4; break; // прилагательное 形容詞長語尾形
        case 2:  wst.prio = 3; break; // глагол 動詞
        case 3:  wst.prio = 5; break; // местоимение 代名詞
        case 4:  wst.prio = 4; break; // местоимение 代名詞(関係詞など?)
        case 5:  wst.prio = 4; break; // местоимение 代名詞?
        case 6:  wst.prio = 4; break; // числительное 数詞
        case 7:  wst.prio = 4; break; // порядковое числительное 順序数詞
        case 8:  wst.prio = 3; break; // наречие 副詞
        case 9:  wst.prio = 3; break; // предикатив 述語／副詞?
        case 10: wst.prio = 1; break; // предлог 前置詞
        case 11: wst.prio = 1; break; // POSL 後置詞?
        case 12: wst.prio = 1; break; // союз 接続詞
        case 13: wst.prio = 1; break; // междометие 間投詞
        case 14: wst.prio = 1; break; // (INP ?)
        case 15: wst.prio = 1; break; // (PHRASE 成句?)
        case 16: wst.prio = 1; break; // частица 小詞
        case 17: wst.prio = 3; break; // краткое прилагательное 形容詞短語尾形
        case 18: wst.prio = 2; break; // причастие 形動詞
        case 19: wst.prio = 2; break; // деепричастие 副動詞
        case 20: wst.prio = 2; break; // краткое причастие 形動詞短語尾形
        case 21: wst.prio = 3; break; // инфинитив 動詞不定形
        defaut:  wst.prio = 1; break;
        }
        if (wst.prio &gt;= maxprio)
            maxprio = wst.prio;
        wv.push_back(wst);
    }
 
    // 最大プライオリティ品詞をもつ候補語のなかで最小長のものを確定
    int minlen = 1000;
    std::vector&lt;std::string&gt; vcand;
    for (wit = wv.begin(); wit &lt; wv.end(); wit++) {
        if ((*wit).prio == maxprio) {
            if ((*wit).len &lt;= minlen) {
                minlen = (*wit).len;
                vcand.push_back((*wit).word);
            }
        }
    }
 
    // 最小長の候補語の最初のものを見出語に決定
    std::string eto; // 選定見出語
    std::vector&lt;std::string&gt;::iterator sit;
    for (sit = vcand.begin(); sit != vcand.end(); sit++) {
        if ((*sit).size() ==  minlen) {
            eto = *sit;
            break;
        }
    }
    std::cout &lt;&lt; "  選定見出語: " &lt;&lt; eto &lt;&lt; "\n";
}
  
// Boost tokenizer により，指定区切り文字で単語を切り出す
void wdtokenizer(std::string& line, std::vector&lt;std::string&gt;& sv)
{
    typedef boost::tokenizer&lt;boost::char_separator&lt;char&gt; &gt; tokenizer;
    // 区切り文字定義 -' を区切りとはしない
    boost::char_separator&lt;char&gt; sep("\t _:;.,?!~`\"\\[]{}()*&^%$#@+=|&lt;&gt;/", 
                    "", boost::drop_empty_tokens);
    tokenizer tok(line, sep);
    for (tokenizer::iterator ti = tok.begin(); ti != tok.end(); ti++) 
        sv.push_back(*ti);
}
 
// 主処理
int main(int argc, char **argv)
{
    tl::lemmatizer lem;         // Lemmatizer instance
    std::string buffer;         // 行バッファ
    std::ifstream in(argv[1]);  // 引数(ファイル名)
  
    if (!in) {
        std::cerr &lt;&lt; "usage: " &lt;&lt; argv[0] &lt;&lt; " file-name" &lt;&lt; std::endl;
        return 1;
    }
 
    // Lemmatizer 文法辞書ロード
    try {
        lem.load_lemmatizer(dict, prdm, pred);
    }
    catch (const std::exception& e) {
        std::cerr &lt;&lt; "Error: " &lt;&lt; e.what() &lt;&lt; std::endl;
    }
   
    while (!in.eof()) {
        std::vector&lt;std::string&gt; v;
        getline(in, buffer, '\n');
        if (buffer.empty()) continue;
 
        // 単語切り出し
        wdtokenizer(buffer, v);
        std::cout &lt;&lt; std::endl &lt;&lt; buffer &lt;&lt; std::endl;
  
        // 単語毎に Lemmatizer 解析を行う
        for (int i = 0; i &lt; v.size(); i++) {
            std::cout &lt;&lt; i &lt;&lt; ". " &lt;&lt; v[i] &lt;&lt; std::endl;
            // lemmatizer analyze.
            try {
                if (!v[i].empty())
                    lem_analyze(lem, v[i].c_str());
            }
            catch (const std::exception& e) {
                std::cerr &lt;&lt; "Error: " &lt;&lt; e.what() &lt;&lt; std::endl;
            }
        }
        v.clear();
    }
 
    return 0;
}</pre>

<p>これを以下の通り GNU C/C++ でコンパイルする。</p>

<pre class="brush: term; gutter: false; ">
% g++ -g -I/usr/local/include -L/usr/local/lib -o lemmatizer \
lemmatizer.cpp -lturglem -lturglem-russian -lMAFSA</pre>

<p>プーシキンの『エヴゲーニイ・オネーギン』の書き出しで試験してみた結果を示しておく。</p>

<p>入力：</p>

<pre lang="ru" class="brush: plain; gutter: false; " title=" А. С. Пушкин «Евгений Онегин» гл. I, ст. I.">Мой дядя самых честных правил,
Когда не в шутку занемог,
Он уважать себя заставил
И лучше выдумать не мог.
Его пример другим наука;
Но, боже мой, какая скука
С больным сидеть и день и ночь,
Не отходя ни шагу прочь!
Какое низкое коварство
Полуживого забавлять,
Ему подушки поправлять,
Печально подносить лекарство,
Вздыхать и думать про себя
Когда же черт возьмет тебя!</pre>

<p>出力：</p>

<pre lang="ru" class="brush: plain; gutter: false;" title="Lemmatizer 処理結果">
Мой дядя самых честных правил,
0. Мой
   [0] МЫТЬ	品詞番号: 2
   [1] МОЙ	品詞番号: 4
   [2] МОЙ	品詞番号: 4
  選定見出語: МОЙ
1. дядя
   [0] ДЯДЯ	品詞番号: 0
  選定見出語: ДЯДЯ
2. самых
   [0] САМЫЙ	品詞番号: 4
   [1] САМЫЙ	品詞番号: 4
   [2] САМЫЙ	品詞番号: 4
  選定見出語: САМЫЙ
3. честных
   [0] ЧЕСТНЫЙ	品詞番号: 1
   [1] ЧЕСТНЫЙ	品詞番号: 1
   [2] ЧЕСТНЫЙ	品詞番号: 1
   [3] ЧЕСТНОЙ	品詞番号: 1
   [4] ЧЕСТНОЙ	品詞番号: 1
   [5] ЧЕСТНОЙ	品詞番号: 1
  選定見出語: ЧЕСТНЫЙ
4. правил
   [0] ПРАВИТЬ	品詞番号: 2
   [1] ПРАВИЛО	品詞番号: 0
   [2] ПРАВИЛО	品詞番号: 0
   [3] ПРАВИТЬ	品詞番号: 2
  選定見出語: ПРАВИЛО
 
Когда не в шутку занемог,
0. Когда
   [0] КОГДА	品詞番号: 12
   [1] КОГДА	品詞番号: 8
  選定見出語: КОГДА
1. не
   [0] НЕ	品詞番号: 16
  選定見出語: НЕ
2. в
   [0] В	品詞番号: 10
  選定見出語: В
3. шутку
   [0] ШУТКА	品詞番号: 0
  選定見出語: ШУТКА
4. занемог
   [0] ЗАНЕМОЧЬ	品詞番号: 2
  選定見出語: ЗАНЕМОЧЬ
 
Он уважать себя заставил
0. Он
   [0] ОН	品詞番号: 3
  選定見出語: ОН
1. уважать
   [0] УВАЖАТЬ	品詞番号: 21
  選定見出語: УВАЖАТЬ
2. себя
   [0] СЕБЯ	品詞番号: 3
   [1] СЕБЯ	品詞番号: 3
  選定見出語: СЕБЯ
3. заставил
   [0] ЗАСТАВИТЬ	品詞番号: 2
  選定見出語: ЗАСТАВИТЬ
 
И лучше выдумать не мог.
0. И
   [0] И	品詞番号: 12
   [1] И	品詞番号: 13
  選定見出語: И
1. лучше
   [0] ХОРОШИЙ	品詞番号: 1
   [1] ЛУЧШЕ	品詞番号: 16
  選定見出語: ХОРОШИЙ
2. выдумать
   [0] ВЫДУМАТЬ	品詞番号: 21
  選定見出語: ВЫДУМАТЬ
3. не
   [0] НЕ	品詞番号: 16
  選定見出語: НЕ
4. мог
   [0] МОЧЬ	品詞番号: 2
  選定見出語: МОЧЬ
 
Его пример другим наука;
0. Его
   [0] ОНО	品詞番号: 3
   [1] ОНО	品詞番号: 3
   [2] ОН	品詞番号: 3
   [3] ОН	品詞番号: 3
   [4] ЕГО	品詞番号: 4
  選定見出語: ОН
1. пример
   [0] ПРИМЕРЕТЬ	品詞番号: 2
   [1] ПРИМЕР	品詞番号: 0
   [2] ПРИМЕР	品詞番号: 0
  選定見出語: ПРИМЕР
2. другим
   [0] ДРУГОЙ	品詞番号: 4
   [1] ДРУГОЙ	品詞番号: 4
   [2] ДРУГОЙ	品詞番号: 4
  選定見出語: ДРУГОЙ
3. наука
   [0] НАУКА	品詞番号: 0
  選定見出語: НАУКА
 
Но, боже мой, какая скука
0. Но
   [0] НО	品詞番号: 12
   [1] НО	品詞番号: 13
  選定見出語: НО
1. боже
   [0] БОГ	品詞番号: 0
  選定見出語: БОГ
2. мой
   [0] МЫТЬ	品詞番号: 2
   [1] МОЙ	品詞番号: 4
   [2] МОЙ	品詞番号: 4
  選定見出語: МОЙ
3. какая
   [0] КАКАТЬ	品詞番号: 19
   [1] КАКОЙ	品詞番号: 4
  選定見出語: КАКОЙ
4. скука
   [0] СКУКА	品詞番号: 0
  選定見出語: СКУКА
 
С больным сидеть и день и ночь,
0. С
   [0] С	品詞番号: 10
  選定見出語: С
1. больным
   [0] БОЛЬНОЙ	品詞番号: 1
   [1] БОЛЬНОЙ	品詞番号: 1
   [2] БОЛЬНОЙ	品詞番号: 1
   [3] БОЛЬНОЙ	品詞番号: 0
   [4] БОЛЬНОЙ	品詞番号: 0
   [5] БОЛЬНАЯ	品詞番号: 0
  選定見出語: БОЛЬНОЙ
2. сидеть
   [0] СИДЕТЬ	品詞番号: 21
  選定見出語: СИДЕТЬ
3. и
   [0] И	品詞番号: 12
   [1] И	品詞番号: 13
  選定見出語: И
4. день
   [0] ДЕТЬ	品詞番号: 2
   [1] ДЕНЬ	品詞番号: 0
   [2] ДЕНЬ	品詞番号: 0
  選定見出語: ДЕНЬ
5. и
   [0] И	品詞番号: 12
   [1] И	品詞番号: 13
  選定見出語: И
6. ночь
   [0] НОЧЬ	品詞番号: 0
   [1] НОЧЬ	品詞番号: 0
  選定見出語: НОЧЬ
 
Не отходя ни шагу прочь!
0. Не
   [0] НЕ	品詞番号: 16
  選定見出語: НЕ
1. отходя
   [0] ОТХОДИТЬ	品詞番号: 19
  選定見出語: ОТХОДИТЬ
2. ни
   [0] НИ	品詞番号: 12
   [1] НИ	品詞番号: 16
  選定見出語: НИ
3. шагу
   [0] ШАГ	品詞番号: 0
  選定見出語: ШАГ
4. прочь
   [0] ПРОЧИТЬ	品詞番号: 2
   [1] ПРОЧЬ	品詞番号: 8
  選定見出語: ПРОЧЬ
 
Какое низкое коварство
0. Какое
   [0] КАКОЙ	品詞番号: 4
   [1] КАКОЙ	品詞番号: 4
  選定見出語: КАКОЙ
1. низкое
   [0] НИЗКИЙ	品詞番号: 1
   [1] НИЗКИЙ	品詞番号: 1
  選定見出語: НИЗКИЙ
2. коварство
   [0] КОВАРСТВО	品詞番号: 0
   [1] КОВАРСТВО	品詞番号: 0
  選定見出語: КОВАРСТВО
 
Полуживого забавлять,
0. Полуживого
   [0] ПОЛУЖИВОЙ	品詞番号: 0
   [1] ПОЛУЖИВОЙ	品詞番号: 0
   [2] ПОЛУЖИВОЙ	品詞番号: 1
   [3] ПОЛУЖИВОЙ	品詞番号: 1
   [4] ПОЛУЖИВОЙ	品詞番号: 1
  選定見出語: ПОЛУЖИВОЙ
1. забавлять
   [0] ЗАБАВЛЯТЬ	品詞番号: 21
  選定見出語: ЗАБАВЛЯТЬ
 
Ему подушки поправлять,
0. Ему
   [0] ОНО	品詞番号: 3
   [1] ОН	品詞番号: 3
  選定見出語: ОН
1. подушки
   [0] ПОДУШКА	品詞番号: 0
   [1] ПОДУШКА	品詞番号: 0
   [2] ПОДУШКА	品詞番号: 0
  選定見出語: ПОДУШКА
2. поправлять
   [0] ПОПРАВЛЯТЬ	品詞番号: 21
  選定見出語: ПОПРАВЛЯТЬ
 
Печально подносить лекарство,
0. Печально
   [0] ПЕЧАЛЬНО	品詞番号: 8
   [1] ПЕЧАЛЬНЫЙ	品詞番号: 1
   [2] ПЕЧАЛЬНО	品詞番号: 9
  選定見出語: ПЕЧАЛЬНЫЙ
1. подносить
   [0] ПОДНОСИТЬ	品詞番号: 21
   [1] ПОДНОСИТЬ	品詞番号: 21
  選定見出語: ПОДНОСИТЬ
2. лекарство
   [0] ЛЕКАРСТВО	品詞番号: 0
   [1] ЛЕКАРСТВО	品詞番号: 0
   [2] ЛЕКАРСТВО	品詞番号: 0
   [3] ЛЕКАРСТВО	品詞番号: 0
  選定見出語: ЛЕКАРСТВО
 
Вздыхать и думать про себя
0. Вздыхать
   [0] ВЗДЫХАТЬ	品詞番号: 21
  選定見出語: ВЗДЫХАТЬ
1. и
   [0] И	品詞番号: 12
   [1] И	品詞番号: 13
  選定見出語: И
2. думать
   [0] ДУМАТЬ	品詞番号: 21
  選定見出語: ДУМАТЬ
3. про
   [0] ПРО	品詞番号: 10
  選定見出語: ПРО
4. себя
   [0] СЕБЯ	品詞番号: 3
   [1] СЕБЯ	品詞番号: 3
  選定見出語: СЕБЯ
 
Когда же черт возьмет тебя! 
0. Когда
   [0] КОГДА	品詞番号: 12
   [1] КОГДА	品詞番号: 8
  選定見出語: КОГДА
1. же
   [0] ЖЕ	品詞番号: 12
   [1] ЖЕ	品詞番号: 16
  選定見出語: ЖЕ
2. черт
   [0] ЧЕРТА	品詞番号: 0
   [1] ЧЕРТ	品詞番号: 0
  選定見出語: ЧЕРТ
3. возьмет
   [0] ВЗЯТЬ	品詞番号: 2
  選定見出語: ВЗЯТЬ
4. тебя
   [0] ТЫ	品詞番号: 3
   [1] ТЫ	品詞番号: 3
  選定見出語: ТЫ</pre>

<p>この例ではまずまずである。プライオリティはもう少し試験して見直しをしなければならないだろう。<span lang="ru">его</span>，<span lang="ru">оно</span> が <span lang="ru">он</span> に集約されてしまう，等は目をつぶるしかない。見出語変換で仮に誤りがあったとしても，用例を人間が視て妥当性の判断は可能だ。正規表現で幅をもたせた条件指定ができれば，そういう根本的問題点を少しはカバーできると思う。それにしても，機械が言語解析をやると面白いことが起こるものである。</p>]]>
        
    </content>
</entry>

<entry>
    <title>歯痛い</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/04/ha-itai.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1145</id>

    <published>2012-04-13T16:20:29Z</published>
    <updated>2012-04-13T17:33:44Z</updated>

    <summary>風邪を引いたからか，3 年前に治療中のまま仕事の都合で完治させず放置していたムシ...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="sports" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="夢ノ中ノ日常" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>風邪を引いたからか，3 年前に治療中のまま仕事の都合で完治させず放置していたムシ歯がまた痛むようになった。今日，仕事を早退して歯医者に行った。もう抜くしかないらしい。炎症を起こして腫れまくっていたので，痛み止めと抗生物質だけ処方してもらい，治療は来週ということに。薬を飲んだら即効で痛みが引いた。いつもながらお医者さん，薬屋さんには感謝なんである。</p>

<p>てなわけで妻よりも早めに帰宅し，猫と少し遊び，洗濯物を取り込み，皿洗いをし，ご飯を炊き，テレビのニュースを観る。北朝鮮のミサイル実験は失敗に終わったようである。発射して 1, 2 分で空中分解したらしい。核弾頭を積んでいたら北朝鮮そのものが被爆していたんじゃなかろうかと想像すると，金もなく技術もまだまだなのにムダなことやってるなーとつくづく思う。今回のミサイル実験は記念すべき金日成生誕 100 周年の一大花火だったわけで，成功したら北朝鮮の核の脅威が現実的になるかもと怖れられた。でも失敗。驚くべきことに今回，北朝鮮は公式に「失敗」を認めたそうである。私にはこちらのほうがその意外さでよっぽど怖い。キムジョンウンドンジケソの立場やいかに。この失敗の汚名を注ごうと，核実験をまたぞろやりそうである。</p>

<p>ま，ミサイル発射は失敗したことだし，政府の発射確認が遅いだのなんだの世間が騒いでいるけれども，私にとってはもうどうでもよいこと。政府も過去 3 回の経験でかなり学習したはずだ。それよりもスポーツ。ニュースのあと，ジャイアンツ VS ベイスターズの野球中継を観た。2 年目・沢村投手の鬼気迫る投球に感心。ジャイアンツの完勝。斎藤ハンカチ王子よりも，彼と同期のこの沢村君を私は高く買っている。解説の元楽天監督・野村さんが，沢村投手の投球フォームと制球力を腐していたのに，そしてゲストの SMAP 中居君を前に「横浜球場の野球中継なんだから巨人ファンをゲストに呼ぶべきではない」とつぶやいていたのに，私は頭に来た。でも，結局，雨天にもかかわらず 1 安打完封という（オマケに 1 イニング 4 奪三振という珍記録付の）素晴らしい投球内容で試合を締めた沢村投手の結果こそが，野村さんの老害ぶりを証明付けた。そこがいちばん痛快であった。阪神もアニキの決勝打で勝ちましたしね。</p>

<p>スポーツといえば，一昨日，ドイツサッカー・ブンデスリーガの今シーズンの天王山の一戦，ボルシア・ドルトムント VS バイエルン・ミュンヘンで，香川選手のドルトムントが見事勝利を納めた。あの金満最強メンバーのバイエルンに競り勝つところ，若くても統制されたチームの素晴らしさだろう。これでドルトムントは 2 位バイエルンに勝ち点 6 差を付けた。残り 4 試合。2 勝 1 分以上で，たとえバイエルンが残り全勝しても優勝が決る。次のシャルケ戦，メンヘングラードバッハ戦のどちらかに勝利できれば，ほぼ間違いなくマイスターシャーレを手にすることができるだろう。あとは勝つ意欲も失せた下位チームとの対戦だからである。</p>

<p>猫のありすはモーツァルトを聴きながらおねんね。癒されまんなー。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="20120413-alice-tt.png" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120413-alice-tt.png" width="486" height="366" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /></div>]]>
        
    </content>
</entry>

<entry>
    <title>北朝鮮，イラン問題</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/04/kitachosen-iran.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1144</id>

    <published>2012-04-10T10:22:31Z</published>
    <updated>2012-04-10T14:47:38Z</updated>

    <summary>北朝鮮のロケット発射予告とイラン核開発問題がかしましい。 今週どこかでＸデーを迎...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="夢ノ中ノ日常" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>北朝鮮のロケット発射予告とイラン核開発問題がかしましい。</p>

<p>今週どこかでＸデーを迎えるはずの北朝鮮ロケット実験は，2009 年にも「人工衛星」と称して行われ，麻生首相が迎撃すると息巻いてちょっと中国，北朝鮮がビビったところをみせた。今回はロシアが非難の気色を強めていて興味深い。米国から食料支援の合意を取り付けたすぐあとに（舌の根も乾かぬうちにと言うのか）旧西側諸国を敵に廻してでもミサイル試験に踏み切るこのクレイジーな国に，どことなく畏敬の念を覚えてしまう私はバチ当りか。</p>

<p>「餓えで死にそうな人がゴマンといるのに何やってんだ」的な，しごくまっとうな意見をもっている日本人は多いと思う。でも，軍事的優位（核兵器＋ICBM 保有は圧倒的優位の条件である）のためには国民の数パーセントは犠牲になってもよいとキム王朝は思っているようである。昔，日本史の教科書に，世界大恐慌後の世相として，餓えに苦しむ子供がダイコンだか何かに齧り付いている東北の田舎の写真が掲載されていて，そのすぐそばに軍国主義の台頭の記述があった。北朝鮮は昔の日本とまったく同じなんである。韓国は官僚機構と大企業支配とを支える日本の超学歴主義的教育を模倣し，北朝鮮は日本の天皇神格化軍国主義を模倣した。彼らは，思うに，かつての日本のパロディである。まるで日本の戦前・戦中と戦後のメンタリティが北と南に別れて争っているかのようである。だからこそ，日本人には彼らの悪い所ばかりが目立つのである。日本もそういう一時期があったからには，北朝鮮を嗤うことが私にはできない。</p>

<p>北朝鮮がテポドンに核弾頭を搭載する技術を獲得したらどうなるのか。今回の実験がもし成功したら，あと数年でその域に漕ぎ着けるかも知れない。ま，日本は何もできない（当たるかどうかもまったくわからない — というか，多分当たらない — 迎撃システムに税金を注ぎ込むのが関の山である）。ま，（いままでどおり）韓国にお任せすればいいんじゃないでしょうか。そうやって北と南がいつまでたってもいがみ合っていてくれてくれたほうが，日本の国家的エゴイストにとってはよいと思う。</p>

<p>核ミサイル開発について北朝鮮とつるんでいるイランについては，あの宇宙人・ハトヤマ元首相がイラン訪問をしたそうである。そして帰国早々，彼が IAEA を批判したとのイラン政府の発表に「そんなことは言っていない，捏造だ」と記者会見しなければならないハメになった。皆「利用されたボクがバカでした」としかこの会見を受けとめていないはずである。野田首相をはじめ政府・閣僚皆から諌止されたにもかかわらず「世界平和と友愛のために」イランに赴こうとする彼の熱意はわかる。でも，この時期イランに「政治利用」されにのこのこ行くところ，どうも日本の政治家らしいナイーブさ（ハトヤマさんはホントにいい人なんだろう）が露になっていて，哀しくなる。日本国外務省のいない密室のようなところでなした会談なんて，どれだけイラン側に都合のよい発表をイラン政府が行おうと，誰も何もツッコめないというのがどうしてハトヤマさんにはわからないのだろうか。</p>

<p>イランは石油資源に関しわが国にとって大事な国であることは疑いがない。しかし，核開発について北朝鮮と同じ道を行く国であって，日本政府が意を決して，米国・EU と足並みを揃えてイラン制裁に踏み切った（というのも日本とイランの関係はきわめて良好だったので，政府は一大決心をしたのである。イランからすれば「ブルータス，お前もか」も同じだろう）いまこの時点では，核開発停止を「強制」する（「話合いでの解決」はこれまでのイラン政府の行動からして無理なのだ。この点においてもイランは北朝鮮とまったく同じである）以外のアクションは，国際社会の不信感を招く。せっかく米国の制裁対象国から外された（これは玄葉外相の政治決断の賜物である）のに，さらにイランに対して余計な追加制裁措置を取らざるを得なくなる（もともと玄葉外相はイラン制裁に反対だった）。民主党内部から造反分子が出まくって，野田さんが可哀想。</p>]]>
        
    </content>
</entry>

<entry>
    <title>靖國・千鳥ヶ淵観桜散歩</title>
    <link rel="alternate" type="text/html" href="http://nox-insomniae.ddo.jp/insomnia/2012/04/kudan-kanou-sampo.html" />
    <id>tag:nox-insomniae.ddo.jp,2012:/insomnia//2.1143</id>

    <published>2012-04-07T15:02:50Z</published>
    <updated>2012-04-07T17:49:07Z</updated>

    <summary>東京・横浜も桜が満開になった。あまり天気がすぐれず薄ら寒い日だったが，妻と靖國神...</summary>
    <author>
        <name>isao</name>
        
    </author>
    
        <category term="liber" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="musica" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="sports" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="夢ノ中ノ日常" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://nox-insomniae.ddo.jp/insomnia/">
        <![CDATA[<p>東京・横浜も桜が満開になった。あまり天気がすぐれず薄ら寒い日だったが，妻と靖國神社・千鳥ヶ淵まで桜を観に行った。昨年は震災のため観桜の余裕はなかったのが思い出された。</p>

<p>九段坂は地下鉄東西線駅舎内からすでに人で溢れ返っていた。靖国通りを跨ぐ歩道橋の人の列がまったく動く気配がないので，先に靖國神社にお詣りすることにした。当然ながらこちらも花見客で混雑していた。大村益次郎銅像のあたりで軍歌の合唱会のようなイベントがあり，ただでさえ混雑しているのに参道がイベント客で埋め尽くされ，通り抜けるのに大いに苦労した。軍服を着たコスプレ老人たちには，吐気がした。遊就館では，開戦 70 周年記念の展示会が催されていた。</p>

<p>拝殿で参拝を済ませ，しばらく花を眺め気を浴びた。神門を潜ってすぐ，能楽堂の前に広がる靖國の桜は，実に見事である。ここが東京の桜の開花指標になっている（ハズである）のも頷ける。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="yasukuni sakura" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120407-yasukuni.png" width="646" height="246" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /></div>

<p>石鳥居から社を出ると，私が入社したときに事業部本部であったビルがみえた。この季節，新入りのころは勤務時間中から花見の場所取りをさせられたなぁという記憶しか甦って来なかった。千鳥ヶ淵に向かう歩道は遅々として進まなかった。インド大使館が桜まつりで賑わっていた。大使館は皇居に近いほど格が高い。半蔵門にある英国大使館はその意味で最高待遇なのである。インド大使館もよい場所にある。</p>

<p>千鳥ヶ淵はおそらくわが国でも有数の観桜名所である。大阪の造幣局，奈良の吉野も素晴らしいが，皇居に接し濠の水を取り巻いて降り注ぐ桜の景観は，私にとって別格である。この季節は夜間もライトアップ演出がなされる。それも拝みたくなった。松井冬子の夜桜の大作（千鳥ヶ淵の桜を描いた『<a href="http://matsuifuyuko.com/works/004.html" target="_blank">この疾患を治癒させるために破壊する</a>』— 先月，横浜美術館でこの目でみて圧倒された）を想像した。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="chidorigafuchi" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120407-chidorigafuchi.png" width="646" height="246" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /></div>

<p>桜を観たあとは，靖国通りを神保町まで歩いた。神保町では千代田さくらまつりに合わせて「さくらブックフェア」が開催されていて，歩道に古書店による露店が出ていた。それらを眺め歩きながら，古典文庫『秋成狂歌集』（丸山季夫編，昭和 47 年刊。古典文庫は会員制の出版社で，日本の古典の印影と校訂テクストを会員のみに提供する珍しいシステムをとっていた），会田雄次『日本人の意識構造』（講談社現代新書，昭和 47 年刊）など，叩き売りの文庫，新書を 4 冊，Orpheus Chamber Orchestra 演奏によるブリテンの Simple Symphony op. 4 の独 Grammophon 盤 CD 1 枚，そして文庫ブックカバーを購入した。このブックカバーは，古書りぶる・りべろオリジナルのようで，緑青（ろくしょう），浅黄（あさき），退紅（あらぞめ）の和の色を組み合わせた絹地に，中務の歌「さくらばなちりかふそらはくれにけりふしみのさとにやどやからまし」が書かれている。西行の筆によるとの言い伝えがあるとお店の人が教えてくれた。</p>

<div align="center" style="margin-top: 24px; margin-bottom: 24px;"><img alt="Jimbocho, bookcover" src="http://nox-insomniae.ddo.jp/insomnia/archives/20120407-jinbocho.png" width="646" height="246" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto;" /></div>

<p>神保町に来ると必ず立ち寄る喫茶店で，苦いマンデリンをいただいた。こんな旨い珈琲を飲んだのはいつ以来か。帰り，妻と近所でラーメンを食った。</p>

<p>帰宅したら，阪神 VS 巨人戦 9 回大詰めの模様がテレビ中継されていた。1-0 でタイガースの勝利。よっしゃー！ 巨人はまたもや完封負け。ジャイアンツ相手だと各チームがエース級を当てて来るのは当然なんだけど，ここまで調子が上がらないのはみていて可哀想になる。それにしても，杉内投手を補強して今年の巨人はひと味違うだろうと思っていたが，杉内が期待通り素晴らしい投球をしながら 1-0 で敗れるのは皮肉である。ま，あれだけの強力打線を擁しているんだから，そのうち上がって来るとは思う。</p>

<div align="center">* * *</div>

<p>Benjamin Britten の Simple Symphony は彼の若書きだけど，弦楽オーケストラによる文字通り若々しくシンプルな旋律と音響で，何とも懐かしい気分にさせてくれる。私の勝手な感想。</p>

<div class="amazlet-box"><div class="amazlet-image"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/B000001G9J/noxinsomyasud-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://ecx.images-amazon.com/images/I/51ySW90GFbL._SL160_.jpg" alt="Bizet: Symphonie No. 1" style="border: none;" /></a></div><div class="amazlet-info"><div class="amazlet-name"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/B000001G9J/noxinsomyasud-22/ref=nosim/" name="amazletlink" target="_blank">Prokofiev, Bizet, Britten - Symphonien</a></div><div class="amazlet-detail">Orpheus Chamber Orchestra<br />Polygram Records (1990-10-25)<br /></div><div class="amazlet-sub-info"><div class="amazlet-link"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/B000001G9J/noxinsomyasud-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jp で詳細を見る</a></div></div></div><div class="amazlet-footer"></div></div>

<p>会田雄次は己の戦争・捕囚体験から日本人の精神構造を真剣に探求した作家である。本書はその代表的論考。私はこの本を，高校一年のとき国語の先生から出された夏期読書感想文課題で読んだ。靖國神社のコスプレ老人を目にしたからか，いま再び読み直したくなった。</p>

<div class="amazlet-box"><div class="amazlet-image"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4061156934/noxinsomyasud-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://ecx.images-amazon.com/images/I/212ETWSMXNL._SL160_.jpg" alt="日本人の意識構造 (講談社現代新書 293)" style="border: none;" /></a></div><div class="amazlet-info"><div class="amazlet-name"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4061156934/noxinsomyasud-22/ref=nosim/" name="amazletlink" target="_blank">日本人の意識構造 (講談社現代新書 293)</a></div><div class="amazlet-detail">会田雄次<br />講談社<br /></div><div class="amazlet-sub-info"><div class="amazlet-link"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4061156934/noxinsomyasud-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jp で詳細を見る</a></div></div></div><div class="amazlet-footer"></div></div>

<p>古典文庫『秋成狂歌集』もアマゾン古書で出ているので，リンクを付けておく。</p>

<div class="amazlet-box"><div class="amazlet-image"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/B000J9835G/noxinsomyasud-22/ref=nosim/" name="amazletlink" target="_blank"><img src="/insomnia/archives/akinari_kyokashu.png" width="110" alt="秋成狂歌集 (1972年) (古典文庫〈第299冊〉)" style="border: none;" /></a></div><div class="amazlet-info"><div class="amazlet-name"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/B000J9835G/noxinsomyasud-22/ref=nosim/" name="amazletlink" target="_blank">秋成狂歌集 (1972年) (古典文庫〈第299冊〉)</a></div><div class="amazlet-detail">上田秋成<br />丸山季夫 編<br />古典文庫<br /></div><div class="amazlet-sub-info"><div class="amazlet-link"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/B000J9835G/noxinsomyasud-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jp で詳細を見る</a></div></div></div><div class="amazlet-footer"></div></div>]]>
        
    </content>
</entry>

</feed>

