« 2007年12月 | メイン | 2008年2月 »

2008年1月28日

Javaで雪の降るアニメーションを作る
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。中村です。

私は大阪出身で小さい頃には雪だるまが作れるくらいに雪が降っていましたが、最近はあまり降らないので淋しく感じます。

ということで、Javaで雪の降るアニメーションを作ってみました。Flashで作る方が今時な気もしましたが、気にせずにJavaのSwingです。

ウインドウ上でマウスを左右に動かすと、それに合わせて雪がなびくようになっています。


snow posted by (C)フォト蔵

せっかくですので、ソースコードを含んだJARファイルを公開します。Java5以上で動作すると思います。

やっていることはJFrameにひとつだけCanvasを配置して、ある一定間隔ごとに雪の配置を動かしたあとに再描画しています。 再描画するタイミングで、雪を落下させるのと、マウスの前回との位置を比較して左右への移動をさせています。

Swingを使ってアプリケーションを開発するのも楽しいですが、たまにはアニメーションを作ってみるのも楽しいですね。


[追記 2008/01/28 20:58]

JFrameで作ってしまったのでSwingと書いてしまいましたが、Swingで統一する場合はCanvasではなくJPanelなどを使うほうが適当だと思います。

2008年1月24日

Symfonyプラグインまとめ ~その2~
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

yukiです。

前回のエントリから大分時間がたってしまいましたが、symfonyプラグインまとめ~その2~をお送りしたいと思います。
今日までに追加された分と未紹介の分についてのご紹介です。

  • Dynamic Generators
    sfCssTabsplugin
    Word Press の管理画面風なタブレイアウト
    sfSavvyPlugin
    symfonyの追加ヘルパー
    sfSiteMapPlugin
    簡易サイトマップ作成
    sfSitemap2Plugin
    sfSiteMapPluginを使ってオブジェクト/配列からXML作成
    sfUIPlugin
    データグリッド作成支援
  • JavaScript
    • Not Based on a JS Framework
      sfAjaxUploaderPlugin
      ajaxを利用したアップロードフォームタグヘルパー
      sfJSONRPCPlugin
      JSON-RPCの利用
      sfLlooggPlugin
      LLOOGGコード埋め込みフィルタ
      sfMilonicPlugin
      Milonicを利用したドロップダウンリスト
      sfPJSPlugin
      actionからjsファイルを動的に生成
      sfUJSPlugin
      actionからjsファイルを動的に生成
      sfUrchinPlugin
      Google Analiticsのコードを埋め込む
    • Dojo
      sfDojoPlugin
      DoJo Toolkitの追加
      sfUnobstrusiveDojoPlugin
      Dojoの利用ヘルパー
    • Ext
      sfExtJSPlugin
      Ext1.x系の利用支援
      sfExtjs2Plugin
      Ext2.x系の利用支援
      sfExtjsThemePlugin
      scaffold生成した管理画面でExtを利用
    • jQuery/jQueryUI
      ddJQueryCalendar
      jQuery calenderの追加
      mqThickboxPlugin
      Thickbox の利用
      sfJqueryPlugin
      Jqueryの利用
      sfTaconitePlugin
      JQuery Taconite Pluginの利用
    • Prototype/Scriptalicious
      dwPrototypeTooltipPlugin
      prototypeベースのツールチップ
      dwPrototypeWindowPlugin
      prototypeベースのウィンドウ・ダイアログ
      sfgWidgetsPlugin
      gWidgets libraryの追加
      sfLightboxPlugin
      LightBox2の利用
      sfLightWindowPlugin
      LightWindow v2.0の利用
      sfModalBoxPlugin
      ModalBoxの利用
      sfNiftyPlugin
      Nifty corner cubeの利用
      sfPrototypePlugin
      デフォルトのprototype.jsを置き換えて使えるようにする
    • YUI
      sfYUIPlugin
      YUI(Yahoo UI Library)の利用
  • 画像・動画・Flash・PDF
    • 共通
      sfMediaLibraryPlugin
      アップロードされたメディアの管理支援
      sfMogileFSPlugin
      MogileFSを利用支援
    • Flash
      dwSwfChartPlugin
      SWFChartsを使ったチャート生成
      sfAmChartsPlugin
      amChartを使ったチャート生成
      sfSIFRPlugin
      sIFRの利用
      sfSwfObjectHelperPlugin
      javascriptを利用したSWFObjectヘルパー
    • 画像
      dwJpgraphPlugin
      JpGraphの利用
      sfChartDirectorPlugin
      ChartDirectorの利用
      sfFlickrGalleryPlugin
      FlickrAPIの利用
      sfGallery2Plugin
      Gallery2の利用
      sfSmiliePlugin
      Wordpress Smiliesの利用
      sfTextReplacementPlugin
      GDを使った文字の画像化
      sfThumbnailPlugin
      アップロードされたサムネイルの生成
    • PDF
      sfDomPDFPlugin
      HTMLをPDFに変換
      sfOpenOfficePlugin
      OpenOffice形式の出力支援
      sfPDFLatexPlugin
      LaTexを利用してPDF出力
      sfTCPDFPlugin
      TCPDFを利用してPDF出力
    • 動画
      sfFLVPlayerPlugin
      FLVプレイヤー

参照元:

2008年1月22日

Emacsを自分で拡張する際のTips
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

ウノウでは少数派なEmacsユーザのbokkoです。

今回は自分でEmacsLisp(以下、elisp)を書いてEmacsを拡張する際のTipsについて紹介します。

拡張する際に気に留めておくこと

Emacsを拡張する上で覚えておくべきなのはEmacs上で行える対話的な動作は「M-x 関数名」で実現できるということです。例えば、C-bはカーソルを1文字分左へ戻しますが、これは「M-x backward-char」とタイプすることと同じです。もしC-bが押しにくい(多分私だけです)のであれば、以下のようなelispを評価してキーバインドを変更することができます。

(global-set-key "\C-l" 'backward-char)

Emacsではこのようなキーバインドに限らず、あらゆる操作をelispを使って変更したり、新たに定義することができます。関数名もしくはキーバインドの一方しかわからない場合は、「help-for-help f」(C-h f)とタイプして関数名を入力したり、「help-for-help k」(C-h k)とタイプしてキーを入力すると、それに対応する関数の説明やキーバインドが表示されますので、これで確かめましょう。

実際の拡張例

次は、最近私が実際に拡張した例について紹介します。(もしかしたら既に同じようなものがあるかもしれません)

find-tagとelscreenの連携

elscreenは簡単に言うとEmacsをタブエディタっぽく使えるようにするものです。人によってはGNU SCREENのEmacs版と言えばしっくりくるかもしれません。ここではタブではなく、スクリーンと呼ぶようにします。find-tagはetagsで作成した関数のインデックスを利用して、指定した関数が定義されているファイルのポイントへジャンプする関数です。しかし、find-tagはデフォルトでは指定した関数が定義されているファイルを開く際、単に今開いているバッファを切り替えるだけです。elscreenを使っている場合、どうせならあたらしいスクリーンで開いてほしいものです。さらに言うと既にそのバッファを開いているスクリーンがあるなら、そっちのスクリーンにジャンプするとか、今開いているバッファが*scratch*バッファなら普通にswitch-to-bufferでバッファを切り替えるようにしたいところです。というわけで、find-tagの関数の中身を見てみます。(コメントは省いています)

(defun find-tag (tagname &optional next-p regexp-p)
  (interactive (find-tag-interactive "Find tag: "))
  (let ((buf (find-tag-noselect tagname next-p regexp-p)))
    (condition-case nil
    (switch-to-buffer buf)
      (error (pop-to-buffer buf)))))

これを見るとswitch-to-bufferという関数にbufという変数を渡しています。プログラマならbufというのはバッファのことだと想像できると思います。switch-to-bufferは現在編集しているバッファを切り替える関数です。ここでしたいのは単にバッファを切り替えるのでなく、新しいスクリーンを作成してそのスクリーン上でバッファを開くことですので、この部分を別の関数に置き換えてやるとよさそうです。しかし、実際には以下のような手順を踏む必要があります。

  • バッファ(buf)が既にどこかのスクリーン上に存在するか調べる
  • 既に存在する場合はそのスクリーン上のバッファにジャンプし、ない場合は新たにスクリーンを作成してその上でバッファを開く

この手順を踏むにはバッファからスクリーン、もしくはスクリーンからバッファを特定したり、指定したスクリーンにジャンプする操作とスクリーンを新規作成する操作が必要です。ここで最初に言ったことを思い出してください。Emacs上で行える対話的な動作は「M-x 関数名」で実現できます。ということはこれらの操作を行うための関数を組み合わせて目的の操作を定義することができそうです。

スクリーンを新規作成する

elscreenはデフォルトではC-z C-cでスクリーンを新規作成できます。このキーバインドが割り当てられている関数を「help-for-help k」(C-h k)で調べてみると、この操作にはelscreen-createという関数が割り当てられています。実際に(elscreen-create)と書かれた行の末尾にカーソルを持っていってC-x C-eとタイプしてみると、新しいスクリーンが作成されるのがわかると思います。(elispコードの末尾にカーソルがある状態でC-x C-eとするとそのS式を評価して、その結果がミニバッファに表示されます)

指定したスクリーンにジャンプする

elscreenを使っていると画面上部に番号とファイル名がセットになったスクリーンのリストが表示されます。別のスクリーンにジャンプするには「C-z 番号」とタイプします。このキーバインドを「help-for-help k」(C-h k)で調べてみると、elscreen-jumpという関数があるのがわかります。試しに以下の関数を実行します。

(elscreen-jump 1)

これで番号1のスクリーンに移動できればいいのですが、エラーが出ます。なので、この関数をもう少し深い追いしてみましょう。elscreen-jumpの中身は以下のようになっています。

(defun elscreen-jump ()
  "Switch to specified screen."
  (interactive)
  (let ((next-screen (string-to-number (string last-command-char))))
    (if (and (<= 0 next-screen) (<= next-screen 9))
        (elscreen-goto next-screen))))

変数名などから推測するに最後にタイプしたキーが0から9の間であればelscreen-gotoという関数が呼ばれるようです。実際に使ってみます。

(elscreen-goto 1)

上記のelispを実行すると、番号1のスクリーンにジャンプすることができます。

バッファからスクリーン、もしくはスクリーンからバッファを特定する

さきほどのelscreen-gotoの引数は数字でした。ということはスクリーンの番号からバッファを特定することができそうです。また、その逆のことをする関数もありそうです。こればかりはhelp-for-helpで探すわけにはいかないので、実際のコードを追ってみるしかありません。そんなわけでelscreenのソースを眺めてみると、elscreen-find-screen-by-bufferという関数が見付かりました。番号が2のスクリーンで開いているファイルがetags.elの場合、以下のelispを実行すると結果は以下の通りになります。

(elscreen-find-screen-by-buffer "etags.el")
2

また、さらに追っていくとelscreen-get-window-configurationという逆のことをする関数も見付かりました。

(elscreen-get-window-configuration 2)
(#<window-configuration> #<marker at 12071 in etags.el>)

結果を見る限り、etags.elのバッファに関する情報と見てよさそうです。あとはスクリーンの一覧が取得できれば、スクリーンからバッファを特定する処理が書けそうです。さらにelscreenのコードを見ていくとelscreen-get-screen-listという関数が見つかります。スクリーンが3つある状態でこの関数を評価すると、

(elscreen-get-screen-list)
(2 1 0)

という風にスクリーン番号のリストが取得できます。
これらの関数を組み合わせて出来上がったのが以下の関数です。

(defun switch-to-elscreen-create (buf)
  (defun create-new-buf (buf)
    (if (equal elscreen-default-buffer-name (buffer-name (window-buffer)))
        (switch-to-buffer buf)
      (elscreen-create)
      (switch-to-buffer buf)))
  (defun switch-to-elscreen-create-inner (screen-list buf)
    (cond ((null (elscreen-get-window-configuration (car screen-list)))
           nil)
          ((equal (car screen-list) (elscreen-find-screen-by-buffer (buffer-name buf)))
           (elscreen-goto (car screen-list)))
          (t
           (switch-to-elscreen-create-inner (cdr screen-list) buf))))
  (if (null (switch-to-elscreen-create-inner (elscreen-get-screen-list) buf))
      (create-new-buf buf)
    t))

上記の処理ではさらに、今開いているバッファが*scratch*バッファなら単純にswitch-to-bufferでバッファを切り替えるようにしています。あとはfind-tag関数内のswitch-to-bufferをこの関数に置き換えてやれば完成です。もしくは、find-tag-elscreenという風に別の関数を定義した方がいいかもしれません。

(defun find-tag (tagname &optional next-p regexp-p)
  (interactive (find-tag-interactive "Find tag: "))
  (let ((buf (find-tag-noselect tagname next-p regexp-p)))
    (condition-case
    nil
    (switch-to-elscreen-create buf)
      (error (pop-to-buffer buf)))))

TAGSファイルをプロジェクト名で指定できるようにする

TAGSファイルを作成しておくと、ソースコードの検索が楽になります。
しかし、TAGSファイルを読み込むにはいちいちそのファイルがあるディレクトリを指定しなければなりません。また、ソースコードの規模が余程大きい場合を除けば、TAGSファイルは一つのプロジェクトに一つあれば十分です。なので、私はTAGSファイルのあるディレクトリを指定するのではなく、そのTAGSディレクトリが関連づけられているキーワードを指定するようにしています。抱えているプロジェクトが一つだけならEmacsを起動すると同時にそのTAGSファイルを読み込んでもよさそうですが、ここでは複数のプロジェクトを抱えていると仮定して話を進めます。

例えば、eigaとphotoというプロジェクトを抱えているとします。それぞれのプロジェクトのTAGSファイルを元にインデックスを参照するには、M-x visit-tags-tableでTAGSのあるディレクトリを指定する必要があります。emacsを起動したディレクトリにTAGSファイルがあれば簡単なのですが、そうとは限りません。そこで、visit-tags-table-keyという関数を作成して、この関数を呼び出した際にeigaとタイプすればeigaプロジェクトのTAGSファイルを読み込み、photoとタイプすればphotoプロジェクトのTAGファイルを読み込むようにしてみました。


まず、visit-tags-tableの中身を見てみましょう。(find-tagと同じく、コメントは省いています)

(defun visit-tags-table (file &optional local)
  (interactive (list (read-file-name "Visit tags table: (default TAGS) "
                     default-directory
                     (expand-file-name "TAGS"
                               default-directory)
                     t)
             current-prefix-arg))
  (or (stringp file) (signal 'wrong-type-argument (list 'stringp file)))
 (let ((tags-file-name file))
    (save-excursion
      (or (visit-tags-table-buffer file)
      (signal 'file-error (list "Visiting tags table"
                    "file does not exist"
                    file)))
      (setq file tags-file-name)))
  (if local
      (set (make-local-variable 'tags-file-name) file)
    (setq-default tags-file-name file)))

visit-tags-tableを呼び出すとミニバッファにVisit tags table: (default TAGS)と表示されます。ここでTAGSファイルが置かれているディレクトリを指定するわけですが、指定したディレクトリの文字列を関数に渡してやる必要があります。Emacsでこの役目を務めるのはinteractiveという関数です。interactiveはその関数が対話的な動作であるということを宣言するための関数ですが、これにある形で引数を渡すと、関数に渡す引数をユーザが入力することができるようになります。ここでは、ディレクトリではなくプロジェクト名を入力してTAGSファイルを読み込むようにしたいので、visit-tags-tableに渡す引数の部分、つまりinteractiveの中を変えてみると良さそうです。

まず、プロジェクト名から各プロジェクトのTAGSファイルを見つけられるようにする必要があります。elispでは連想配列(のようなもの?)が使えるので、プロジェクト名をインデックスに使います。

(defvar tags-file-list '(
               ("eiga" . "/home/bokko/eiga/")
               ("photo" . "/home/bokko/photo/")
               ))

次に、interactive関数の中身に注目します。

(interactive (list (read-file-name "Visit tags table: (default TAGS) "
                   default-directory
                   (expand-file-name "TAGS"
                             default-directory)
                   t)
           current-prefix-arg))

パッと見ても何をやってるのかよくわかりませんが、default-directoryとexpand-file-nameが何なのかわかれば大体の見当は付きそうです。この2つを評価してみます。

default-directory
"~/"
(expand-file-name "TAGS" default-directory)
"/home/bokko/TAGS"

~/はEmacsを起動したディレクトリです。また、expand-file-nameは相対パスを絶対パスに変換してくれるようです。以上のことからディレクトリを入力するのではなく、プロジェクト名を入力するようにして、そのプロジェクトに対応するディレクトリにあるTAGSファイルを読み込む関数は以下のように定義できます。

(defun visit-tags-table-key (file &optional local)
  (interactive "sTags-key: ")
  (defun find-tags-directory (tags-key file-key-list)
    (if (equal tags-key (car (assoc tags-key file-key-list)))
    (cdr (assoc tags-key file-key-list))
      nil))
  (setq file (expand-file-name "TAGS"
                   (find-tags-directory file tags-file-list)))
  (or (stringp file) (signal 'wrong-type-argument (list 'stringp file)))
  (let ((tags-file-name file))
    (save-excursion
      (or (visit-tags-table-buffer file)
      (signal 'file-error (list "Visiting tags table"
                    "file does not exist"
                    file)))
      (setq file tags-file-name)))
  (if local
      (set (make-local-variable 'tags-file-name) file)
    (setq-default tags-file-name file)))

プログラミングしながら環境構築

このようにEmacsではelispで自分好みの開発環境をプログラミングしながら整えることができるので、非常にかゆいようなところでも手が届くようになります。例えば、WebアプリケーションをMVCなフレームワークを利用して開発していると、アクションに相当するファイルを開いている時にビューのファイルを別のスクリーンで開くような仕組みが欲しくなります。最初からそういう機能をエディタ側で持っていることはまずないですが、その気になれば自分で作ることができます。MVCなフレームワークではディレクトリ階層が深くなりがちでアクションとビューのファイルを開くのにいちいち下り坂と上り坂を行き来するようなことをしないといけないことが多々あるのでこれは是非欲しい機能です(実際、rails.elのようにそれに似たような支援を行うものも存在します)。私の場合、社内で使っているフレームワークで似たような状況に遭遇したので、つい最近、簡単なelispを書いてアクションとビューをコマンド1つで(elscreen上の)別々のスクリーンで開けるようにしました。

参考文献

Emacsの基礎を骨の随まで叩き込みたい人は前者を読むのがいいと思います。また、後者は実際にEmacsを拡張する際にリファレンスとして手元に置いておくと幸せになれるかもしれません。

圧縮アルゴリズム
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

尾藤正人(a.k.a BTO)です

コンピュータを使ってる方ならいつもお世話になってるデータ圧縮。 gzipのようなツールで意識して圧縮していることもあれば、フォーマット自体に圧縮機能が備わっていて、意識しないで使っているケースもあるかと思います。 毎日のようにお世話になってるデータ圧縮ですが、その原理を知らない方も多いのではないでしょうか。 かくいう僕自身も、つい最近までは全く知りませんでした。

そこで、先日の社内勉強会で圧縮アルゴリズムについて一通りやってみました。 その資料を公開します。 僕も専門家ほど詳しいわけでもなく、単に勉強してみただけのくちなので、いろいろおかしな点もあるかもしれません。 何かありましたら、いろいろご指摘いただければと思います。

プレゼン資料の作成にはデータ圧縮法概説を大いに参考させていただきました。 参考っていうか、ほとんどそのまんまです。 ぶっちゃけデータ圧縮法概説を直接読んだ方がちゃんと理解できるので、ぜひ一読をお勧めします。

プレゼン資料

プレゼン動画

2008年1月17日

画像の遅延読み込み
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

yamaokaです。

webページの表示を高速化する手法にはいろいろありますが、 その一つとして遅延読み込みという手法があります。 初期状態で表示する必要のない要素については読み込まず、 必要になったタイミングで読み込み、表示するようにする手法です。

ページの読み込みにかかる時間の大半を 画像の読み込みが占めている場合が多いので、 画像の読み込みを遅延させるという手法が多く取られます。

検討するべきケース

では、画像の遅延読み込みはどのような場合に検討されるべきでしょうか。 最初から表示されている必要がない画像が存在し、その画像のサイズが大きかったり、 そうした画像の数が多い場合は検討してみる価値があると思います。

例えば、次のようなケースです。

  • 初期状態では表示されないブロックに属する画像が存在し、 JavaScriptで表示するかしないかを切り替えているような場合
  • ページのずーっと下の方に画像があって、 その画像は画面をスクロールしないと見えないような場合

仕組み

では、どのように画像の遅延読み込みを実現すればよいのでしょうか。 仕組みは単純です。

  1. 適当なタイミングで、読み込みたくない画像のimg要素のsrc属性の値を適当な画像(1ピクセル四方のGIF画像など)のパスに置き換える
  2. 表示が必要になったタイミングで、先ほど置き換えたsrc属性の値を元に戻し、実際の画像を読み込んで表示させる

これだけ。簡単ですね。

一番目の「適当なタイミング」とは、 ページ全体のDOMツリーの読み込みが完了したタイミングがいいでしょう。 そのタイミングで画像のimg要素のsrc属性を書き換えてしまうことにより、 その時点では不要な画像の読み込みを抑えることができます。 場合によっては、画像が属するブロックの読み込みが完了したタイミングで 実行してしまってもいいかもしれません。

二番目の「表示が必要になったタイミング」とは、 表示されていなかった要素が表示されるタイミングとか、 画面がスクロールされてきたとかそういうタイミングを指します。 そのイベントを検知して、画像を表示させればよいのです。

具体例

具体例として、idが「foo」のブロック要素に含まれる画像を遅延読み込みさせる場合を考えてみます。 prototype.jsをライブラリとして利用している場合、 次のように遅延読み込み処理を記述することができます (便宜上、「Foo」を名前空間代わりに使用しました)。

Foo = {};
Foo.images = [];
Foo.stopLoadingImages = function() {
  $$("#foo img").each(function(image) {
    image.orgSrc = image.src;
    image.src = "1x1.gif";   // 小さな画像に置き換え
    Foo.images.push(image);  // 置き換えた要素をとっておく
  });
};
Foo.loadImages = function() {
  Foo.images.each(function(image) {
    image.src = image.orgSrc;  // 元の値に戻す
  });
};

あとは、「Foo.stopLoadingImages」と「Foo.loadImages」を 必要なタイミングで実行するようにするだけです。

ライブラリの紹介

実際には、何らかのライブラリを利用して遅延読み込みを実現する場合が多いでしょう。 主なライブラリとして、次のようなものがあります。

それぞれのライブラリでできることに違いがありますので、 既に自分が使っているライブラリとの適合性も含め、一度見てみるといいかもしれません。

まとめ

体感速度を少しでも速くするための手法として、画像の遅延読み込みは効果的です。 初期状態では画像に通常のimg要素が用いられるため、 JavaScriptが有効になっていない環境で閲覧された場合でも、何の副作用もありません。 必要に応じて導入を検討してみるといいのではないでしょうか。

2008年1月18日12:50追記
「仕組み」の「適当なタイミング」について加筆訂正を行いました。

2008年1月11日

気分をかえてウノウで趣味の開発してみませんか?
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

Keitaです。

週末などに、趣味でコーディングをする時気分を変えて、時々喫茶店にいって開発したりする人もいると思います。僕も、一度、変わった所で開発をしてみようと公園でノートパソコンを広げて開発してみたことがありますが、残念ながら、その日は太陽の光が強く液晶ディスプレイがほとんど見れないという問題にあって、挫折したことがあります。

さて、そういうこともあり、週末会社があいている時間を利用していろいろな人と一緒に、内容はばらばらでも開発してみたいよねという話を社内でしてみたところ意外と盛り上がり、うまいこと許可もとれたので、一日だけ皆さんの開発場所としてウノウを提供させていただこうと思います。

イベント名: サタデー・コード・フィーバー
日時: 2008年2月16日 10:00~
最大人数: 20人まで

タイムスケジュール
- 10時~11時集合(順次集合してもくもくと開発
- 12時~14時  自己紹介後、みんなでご飯
- 18時     終了(しゃべりたい人だけ一人3分くらいの発表)
- 19時 宴会


# セキュリティの関係などで、来社にお名前等を所定の用紙に記載いただきます。


2008/01/11 16:52 追記
申し込み人数に達しましたので、受付を終了させていただきました。
また何かのイベントを開くことあればよろしくお願いいたします。

はてなスターへのURLが間違っており、先ほど、修正したため今までつけたものが一部表示されてないかと思います。大変申し訳ありませんでした。

2008/01/11 18:30追記
申し込みされた方へメールを送信させていただきました。 場所等については、(ウノウですが)後日詳細をおくらせていただきます。

2008年1月 7日

PHPとMecabでキーワード自動リンクを実装する
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは、山下です。
今年もどうぞよろしくお願い致します。

Webサービスを開発していると、特定のキーワードを自動でURLリンクにする処理が必要になることがあると思います。今回は、このキーワード自動リンク機能を形態素解析ツールMecabを使ってPHPで実装する方法を紹介したいと思います。

説明に入る前に少し補足しておくと、Trieの実装であるDouble-Array処理だけ利用したいのであれば、MecabからDouble-Array処理の部分を切り出したDartsというライブラリがあります。しかし、なぜMecabを使うかというと、PerlだとDartsのバインディングが公開されているのですが、現時点でPHP版はありません。また、最近のLinuxディストリビューションでは、Mecabのパッケージが最初から用意されているため、より簡単に利用できると思ったからです。

それでは、順を追って説明していきます。

1. mecabインストール

Fedora 8の場合だと、次のようにしてインストールできます。

# yum install mecab mecab-ipadic mecab-devel

2. 自動リンク用辞書の作成

辞書用のディレクトリを作って、単語辞書(url.csv), 連接表(matrix.def), 未知語の文字定義(char.def), 未知語用品詞定義(unk.def), 設定ファイル(dicrc)の5つのファイルを用意します。

# mkdir /usr/lib/mecab/dic/autolink
# cd /usr/lib/mecab/dic/autolink

まずキーワードとURLの対応を記述したファイル url.csv を作成します。文字コードに注意してください。ここでは、UTF-8で記述しました。

url.csv

Google,0,0,-5878,http://www.google.com/
Yahoo,0,0,-4472,http://www.yahoo.com/
ChaSen,0,0,-5878,http://chasen.org/
京都,0,0,-3200,http://www.city.kyoto.jp/


その他のファイルの中身は次のようにします。これらのファイルは末尾に改行があるとエラーになるので、入れないようにします。

matrix.def

1 1
0 0 0

char.def

DEFAULT 1 0 0
SPACE 0 1 0
0x0020 SPACE

unk.def

DEFAULT,0,0,0,*
SPACE,0,0,0,*

dicrc

cost-factor = 800
bos-feature = BOS/EOS
output-format-type=autolink

node-format-autolink = <a href="%H">%M</a>
unk-format-autolink = %M
eos-format-autolink = \n

辞書のコンパイルを行います。

# /usr/libexec/mecab/mecab-dict-index -f utf-8 -c utf-8
./pos-id.def is not found. minimum setting is used
reading ./unk.def ... 2
emitting double-array: 100% |#################################| 
./pos-id.def is not found. minimum setting is used
reading ./url.csv ... 4
emitting double-array: 100% |#################################| 
./matrix.def is not found. minimum setting is used.
reading ./matrix.def ... 1x1

done!

ここで、問題なく作成されているか確認します。次のようにリンクが張られば成功です。

$ mecab -d /usr/lib/mecab/dic/autolink
そうだ京都、行こう。
そうだ<a href="http://www.city.kyoto.jp/">京都</a>行こう。


次にこれをPHPから使ってみましょう。

3. php_mecabインストール

PHPからMecabを利用するための拡張モジュールは、rskyさんが作られているので、有り難く使わせて頂きます。

$ wget http://www.opendogs.org/pub/php_mecab-0.3.0.tgz
$ tar xzvf php_mecab-0.3.0.tgz
$ cd php_mecab-0.3.0
$ /usr/bin/phpize
$ ./configure --with-php-config=/usr/bin/php-config ¥
--with-mecab=/usr/bin/mecab-config
$ make
$ sudo make install

4. 動作確認

サンプルとして次のようなプログラムを書いて実行してみます。

<?php
dl('mecab.so');
$options = array('-d', '/usr/lib/mecab/dic/autolink');

$t = new MeCab_Tagger($options);
$str = '京都についてGoogleとYahooで検索した。';

print $t->parse($str);

実行結果

<a href="http://www.city.kyoto.jp/">京都</a>について
<a href="http://www.google.com/">Google</a>と
<a href="http://www.yahoo.com/">Yahoo</a>で検索した。


以上、高速なキーワード自動リンクを簡単に実装できることが分かっていただけたかと思います。エントリ執筆にあたり、下記のページを参考にさせて頂きました。この場を借りてお礼を申し上げます。


関連リンク:


SaaS提供の高性能CMS RCMS
SaaS提供の高性能CMS


About 2008年1月

2008年1月にブログ「ウノウラボ Unoh Labs」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2007年12月です。

次のアーカイブは2008年2月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

ウノウサービス