2011年12月18日日曜日

HTA(HTML Application)で使われるJavaScriptエンジン

JavaScript エンジン Chakra を無理矢理使う。という記事を見て、IE9環境においてHTAで使われるJavaScript engineのバージョンが何か気になったので調べてみました。


HTAはWindows環境であれば特別な実行環境が不要なため、Windows環境で簡単な処理をやりたい、でもコマンドプロンプトは嫌だという人に使ってもらうアプリの環境として重宝しています。


調べた結果ですが、Chakraが使われるようです。ただ、何も言わないと5.7互換モードのようなので、X-UA-CompatibleでIE8/IE9モードを指示します。これでJSON.parseとかも使えるようになります。


ソース


結果

2011年12月7日水曜日

Python2からPython3へ

この記事は2011 Pythonアドベントカレンダー(Python3)8日目のエントリーです。@shimizukawaさんから指名されました、@hideaki_t/高橋です。

はじめに

igo-pythonやwhooshのPython3対応を行ったときに実際に問題となった、Python2とPython3の違いとその対応方法を書きたいと思います。なお、私の経験を元にしているため、2と3の差異の一部しかカバーしていません。
私はどちらもPython2/3両対応としたため、既存のPython2対応モジュールを3にも対応させるときに参考になればと思います。

2から3の変更点についてはすでに数回触れられている上、前日の@shimizukawaさんの記事とのかぶりが多くなっています。実際の作業においては、清水川さんの記事が参考になると思います。

と書いていたのですが、順序比較について調べていたら時間が無くなってしまいました…。

問題の分類

まず、発生した問題を分類してみました。
  • そのままでは実行できない・すぐエラーがわかるタイプ
    • SyntaxError系
      • 文字列のUnicode文字化
      • printの関数化
    • NameError系
      • 型・関数名の削除
  • 実行できるけど結果が変わる・エラーに気付かないかも知れないタイプ
    • 引数が受け付ける方が変わっている
    • 順序比較のルールが変わった

そのままでは実行できないタイプ

1. デフォルトの文字列がUnicode文字列になった

Python3では文字列がデフォルトでUnicode文字列となるため、StringPrefixが不要になりました。そのためPython2でのUnicode文字列リテラル(u"文字列")がSyntaxErrorになります。
この問題は、uを除去するだけで解決できますが、Python2.xとの互換を取るときには関数化するなどの工夫が必要になります。

2. printが文から関数になった

printが関数になったので、関数呼び出しの書式にする必要があります。とりあえず動けばいい場合、最も簡単な2/3両対応の方法は括弧'()'で囲むことです。3の場合は関数呼び出しに、2の場合はprintの対象がタプルになります。 非ASCII文字を含む出力などで期待通りの出力を得るには、関数化が必要になると思います。私はsys.stdout.writeで出力する関数を作りました。

3. 型・関数・モジュール・クラスなどの、削除・改名

  • unicode/unichr関数が無くなりました
  • 組み込みのfile型/file関数が無くなりました。ファイルはopenで開く必要があります(2.4までは2.2でfile関数が追加され、openは互換のためにfileのaliasであると書かれていました。2.5でopenが望ましいとされました)

実行できるけど、結果が変わる・実行時エラーが出るタイプ

NameError系もここに分類されますが…

1. 整数同士の割り算('/' 演算子)の結果が、整数ではなく浮動小数点数になった(PEP238)

Python2と同じように、整数同士の割り算で剰余を切り捨てた商が欲しい場合は、'//' 演算子を使う必要があります。Python2でも from __future__ import division すれば '//'演算子は使えるので、これを使いました。
これは、配列の添え字に使うなど整数が必須な場面があるとエラーが起きます。

2. 文字列ではなくbytesを受け取るようになったもの/文字しか受け付けないもの

  • ord - 文字しか受け付けません。bytesの場合は各要素が整数値なので先頭要素を取ればいいだけです
  • struct.unpack - bytesしか受け付けません(正確ではありません。後で補足します…)

3. 順序比較の変更

ここだけかぶってなかったので調べました。

Ordering Comparisonsに変更点があります。関係ないオブジェクト同士の比較などはエラーが起きるようになりました。この変更により、1<"1" や 1<Noneのような比較をするとTypeErrorが発生します。

Python 2.7.2 (default, Nov 21 2011, 17:25:27)
[GCC 4.6.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 1<None
False
>>>

Python 3.2.2 (default, Nov 21 2011, 16:50:59)
[GCC 4.6.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 1<None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < NoneType()

この影響で初期値にNoneを設定しておいて、比較するような処理が書けなくなりました。なので、None以外でその条件を満たす取り得ない値がない場合は、少し面倒です。

また、自作クラスでもそのままでは比較できません。そのクラスのオブジェクトの順序比較をしたい場合には、__lt__(self, other)メソッドの定義が必要になりました。また、sorted, list.sortに比較関数(cmp)が渡せなくなっているため、sortをしたいときにも__lt__(self, other)の定義が必要です。

どこでTypeErrorが出るのかを追ってみました。まずdisモジュールで単純な比較処理を見ると、Python2とPython3で違いはありませんでした。

>>> import dis
>>> dis.dis(lambda: 1<None)
  1           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               0 (None)
              6 COMPARE_OP               0 (<)
              9 RETURN_VALUE      


ソースを追う必要がありそうなので、object.c:do_richcompareを読んでみました。
オブジェクトの型の関連をチェックしていて、比較の右辺が左辺のサブクラスになっている場合は順序比較が出来そうです。また、左辺(逆順の場合は右辺)が比較可能の場合も比較は可能のようです。

__lt__なし
>>> class C: pass
... 
>>> C()<C()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: C() < C()


__lt__あり
>>> class C:
...  def __lt__(self, x): return False
... 
>>> C()<C()
False

別クラスのオブジェクトの比較
>>> class D: pass
... 
>>> C()<D()
False
>>> D()<C()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: D() < C()
最初の方は左辺側が順序比較可能になっているためエラーになりません。

サブクラスのオブジェクトとの比較
>>> class D(C): pass
... 
>>> C()<D()
False
>>> D()<C()
False

どちらも成功します。

ただ、比較可能なものでもエラーになることがあるのは追い切れませんでした。
例 1は比較可能だがNoneとは比較できない
>>> 1<2
True
>>> 1<None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < NoneType()


実際にはNone以外で問題になることは殆ど無いと思います。

まとまりもないですが、私の分はここまでにしたいと思います。
つぎは、@lirisさんよろしくお願いします。

2011年12月3日土曜日

Arch Linuxでvmwgfxを使う

いまは簡単になっていますので、最近のvmwgfx(Arch Linux)にメモを書きました。


Arch Linux on VMware Player4.0.1でvmwgfxを使うまでのメモ。
1. VMWARE gfx kernel driverをもってくる(masterがvmwgfx 2.xになっているのでvmwgfx_2_0_branchではなくなった)

ソース取得後、makeして出来たkoを置く。
$git clone git://anongit.freedesktop.org/mesa/vmwgfx
$ make && gzip vmwgfx.ko && sudo mv vmwgfx.ko.gz /lib/modules/$(uname -r)/kernel/drivers/gpu/drm/vmwgfx/
2. 新しいSVGAドライバとXAが必要なので、aur/mesa-fullをインストール。
$yaourt -S mesa-full
不要なドライバを外すためPKGBUILDを編集(Gallium版SVGAドライバとXAを有効にする)
編集
 --with-dri-drivers=i915,i965,nouveau,radeon,r200 \
 --with-gallium-drivers=r300,r600,nouveau,svga,swrast \
を次のようにする。
 --with-dri-drivers= \
 --with-gallium-drivers=svga,swrast \
次のオプションをconfigureに追加
 --enable-xa \
あとはビルドしてインストールすればよい。
3. xf86-video-vmwareのvmwgfx_branchを準備
git clone git://anongit.freedesktop.org/xorg/driver/xf86-video-vmware
cd xf86-video-vmware
git checkout vmwgfx_branch
$ ./autogen.sh --prefix=/usr
$ make
$ make install
4. /etc/X11/xorg.conf.dに20-vmwgfx.confなんて名前でファイルを作成する
Section "Device"
 Identifier "SVGA Grapics Device"
 Driver "vmwgfx"
EndSection
5. glxheads, glxinfoなどで確認する
$ glxinfo | grep renderer
OpenGL renderer string: Gallium 0.4 on SVGA3D; build: RELEASE;

2011年11月30日水曜日

WhooshのPython3対応

Python mini hack-a-thonで取りかかったWhooshのPython3再対応ですが、とりあえずPython 2.5.6, 2.7.2, 3.2.2で最小の動作確認したのをpushしました。もう少し様子を見てから投げてみようと思います。

変更点はbyte/strの違いと順序比較ぐらいですかね。

Whooshbitbucketにリポジトリがあるので、bitbucketのアカウントを作ってみました。
Mercurialは少しだけしか使ったことがありませんでしたが、ちょっと使う分には、どれもそれほど違いはないので困ることは無いですね。

2011年11月27日日曜日

(第13回)Python mini Hack-a-thonに参加しました

(第13回)Python mini Hack-a-thonに参加しました。
初めての参加でしたが、Hack-a-thonというイベントの性格上、すんなりなじめたかと思います。いろんな人が居ておもしろいですね。少人数だと誰が何やってるか把握できるのがいいと思いました。

私はwhoosh-igoのPython3対応をすることにしました。(意外とigo-pythonが知られていたようで嬉しかったです)

Whooshは2.0でPython3に対応しました。またigo-pythonも0.9でPython3対応しました。なので、whoosh-igoがPython3対応すればPython3でもwhoosh+igo-pythonで全文検索出来るようになるはずです。

結局、時間内にはwhoosh+igo-pythonで全文検索は出来ませんでした。whoosh-igo自体のPython3対応はおおむね出来ていたのですが、最近のwhooshがどうもPython3で動かなくなっているようで、最小のテスト(whoosh単体にASCII文字だけ使った)でもPython3で動きませんでした。
まだPython2の利用が多いのが原因でしょうか。Python2.5で動かなくなったというissueの対応でPython3で動かなくなるような対応が入っていたりするので…。

whoosh-igoを動かして出た問題をつぶしていくというのが何度か続いたわけですが、まだ終わっていません。終わったらpull request投げようかと思います。

2011年10月19日水曜日

Python Developers Festa 2011.10に参加しました

Python Developers Festa 2011.10に参加しました。
同日開催のTokyo.SciPy #2に参加するため、午前だけの参加となりました。
今回も楽しく過ごせたので、いつも主催や準備などをされている方には感謝しています。



今回、いくつもやりかけで放置されているのを課題として持ち込んで、黙々と作業をしようと思っていたのですが、@nobonoboさんのハンズオン「cocos2d で遊ぼう」がおもしろそうだったので、急遽参加することにしました。
ハンズオンに参加したのが一人だったので、講師を独り占めしてしまいました。ありがたいことです。ゲームの話が出来て楽しかったです。

午前中のみで、さらに私の準備がまずかったのですが、ゲームを作るのに最低限のところまではいけたと思います。


やったこと
  • cocos2dのオブジェクトの説明
    • director, scene, layer,  sprite, menu
  • オブジェクトの関連についての説明
  • イベント・イベントハンドリング
  • アクション
  • アニメーション
  • スクロール・多重スクロール(parallax)
  • スケルトン(skeleton)
  • パーティクルもあるよ
  • タイルマップ(Cocographというエディタが使いやすい)
  • あたり判定は標準でも用意されているが、ゲームのタイプによっては、性能や実現したいこと次第で自分で作る必要がある。動きの量が大きいときは障害物を突き抜けたりするので考慮が必要
  • 動きの量に応じたアニメーション(アクションゲームの歩きパターンとか)は標準で用意されていないので自分で作る必要がある
  • フレームレートを意識する
  • まずtestやsampleを見るのがおすすめ。各オブジェクトや機能の使い方がわかる
  • PyWeek が参考になる

少しですがゲームのプログラムについて知識があったのでなんとなく、用語の意味がわかったので、cocos2dのオブジェクトや機能を知る上で役に立ちました。
おもしろいなーと思ったのは、アクションのstepにdt(前回からどれだけ経過しているか)というパラメータがあることです。普通のGUIのイベントハンドラでは見かけないので(発生時間は入ってたりしますが)


反省
cocos2dをArch Linuxのパッケージからインストールしておいたのですが、VM上の環境ではOpenGLまわりの問題で動きませんでした。ホストのWindows側にインストールしてハンズオンは継続できましたが、時間のロスがもったいなかったです。

帰った後VMware(ソフトウェアレンダラ)に環境を移して試しましたが、別のエラーがでてダメでした。その後Mesaをgitからもってきてビルドしたら、accelerationなしで動くようにはなりました。どうもvmwgfx_drvと新しいDRMなら3D accelerationが効くようになってきているようなのですが、DRMのバージョンが合わずあきらめて、古いドライバ+新しいソフトウェアレンダラで動かしてます

2011年9月1日木曜日

igo-python 0.9をリリースしました

igo-python 0.9をリリースしました。
やっとPython3対応(確認は3.2のみ)をしました。同じコードでPython 2.5/2.6/2.7でも動きます。また、致命的なのですが、(オリジナルのJava版と比較して)正しい解析結果にならないことがあったのを修正しました。バグを探す課程で、igo-0.4.3ベースになりました。


python2/3両対応で変更した点

  • 整数の割り算を/から//に変更した(整数/整数がfloatになった。//は切り捨て)
  • unicodeまわり
  • strはunicodeになり、chrもunichr相当になった
  • unicode文字列リテラルが使えない(3から普通の文字列リテラルがunicode文字列リテラルになるので、u'文字列'ができなくなったという意味です。誤解を招く書き方でした)
  • file型がなくなったので継承できなくなった(io moduleは2.6からなので継承はあきらめた)
Python3対応に関係ないけど変えたもの
  • sys.byteorderの値を使ってendiannessを調べるようにした(Python3でstruct.packの戻り値の方がstrからbytesに変わった。なので各要素が整数値になった。ここら辺を調べていたら、そもそも標準でByte orderを示す値が用意されていた…)
  • データ型指定のフォーマット文字列で、nativeではくstandardなものを選択した(使っているのはint/shortだけなのであまり問題ないけど、LP64な環境だと多分ダメ)

解析結果がおかしい問題は、青空文庫から適当にもってきたファイルを解析してて気付きました。どちらもリポジトリに追加してあります。

青空文庫のxhmltをlxmlで一気にテキスト化するスクリプト
標準入力を形態素解析して、mecabのデフォルト形式で出力するスクリプト

これらをこんな感じでつないで使います。
curl http://www.aozora.gr.jp/cards/000148/files/789_14547.html | python2 aozora_html2txt.py | python parse.py 

2011年8月16日火曜日

solandraを使ってみた

Solr + CassandraSolandraを使ってみました。まだ単純に使っただけです。
Solr使うのと殆ど違いはないのですが、メモしておきます。

Solandraについては、Cassandra SF  2011で使われたスライドがわかりやすいです。 Scaling Solr with Cassandra

1. まずSolandraをビルド。solandra-gitがArch LinuxのAURにあったのでそのまま使いました。

2. igoを使うために必要なjarをlib(/usr/share/solandra/libだった)に置く

  • igo-0.4.3.jar
  • lucene-gosen-1.2-dev-ipadic.jar
  • IgoAnalyzer.jar

3. solandra起動

4. schemaを作る(ベンチマークのを書き換えました)
<?xml version="1.0" encoding="UTF-8" ?>
<schema name="igo" version="1.1">
<types>
<fieldType name="string" class="solr.StrField"/>
<fieldType name="text" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="net.sf.igoanalyzer.solr.IgoTokenizerFactory" dicPath="/home/hideaki/works/dat/ipadic">
</tokenizer>
</analyzer>
</fieldType>
</types>
<fields>
<field name="id" type="string" indexed="true" stored="true"/>
<field name="type" type="string" indexed="true" stored="true"/>
<field name="text" type="text" indexed="true" stored="true"/>
</fields>
<defaultSearchField>text</defaultSearchField>
<uniqueKey>id</uniqueKey>
</schema>
5. schemaの登録
curl http://localhost:8983/solandra/schema/igo --data-binary @schema.xml -H 'Content-type:text/xml; charset=utf-8' 
 6 . 投入するデータを作る
CSVで作りました。次のような感じで。
id,type,text
1,A,ほげほげ

7. データ投入
curl http://localhost:8983/solandra/igo/update/csv?commit=true --data-binary @test.csv  -H 'Content-type:text/plain; charset=utf-8'

8. 検索
curl 'http://localhost:8983/solandra/igo/select?q=%E5%A4%A9%E6%B0%97'

2011年8月14日日曜日

verify:noneの意味

調べるって言ってたやつをみた。

-Xverifyで指定できるのは次の3つ。
  • -Xverify:all
  • -Xverify:none
  • -Xverify:remote
remoteがデフォルトで、ネットワーク経由でロードしたクラスのみverify対象。all/noneはそのままの意味。

2011年7月29日金曜日

調整さんで出席者数をカウントするbookmarklet

調整した後、予約するときとかに人数数えるのが面倒なので書いてみた。カウント

javascript:(function(){var d=document,p=['○','△'],c,i=1,j,k,u,v,t=d.querySelectorAll('#nittei tr');for(;i<t.length-1;i++){u=t[i];v=u.querySelectorAll('td');k=c=0;for(;k<2;k++){for(j=1;j<v.length;j++)c+=(v[j].innerHTML==p[k]);v[0].innerHTML+='|'+c;}}})()

2011年7月25日月曜日

curlは便利

cURLは標準で多くのプロトコルをサポートしていますが、サポートされていないプロトコルでもtelnetで話せる相手なら、telnetと同じように話せるので便利。

memcacheの場合こんな感じで使えます。
echo -e 'stats\nquit' | curl -s -T - telnet://localhost:11211 | egrep 'hits|misses'
(echoがバックスラッシュによるエスケープを処理してくれるものじゃないと、上の例は動きません。bash/zsh組み込みのechoは可)

簡単な処理をシェルスクリプトでする程度なら、これでも十分使えますね。

2011年7月19日火曜日

javaのFutureTask.getがブロック/起きる仕組み

第2回JVMソースコードリーディングの会に参加しました。
最初のクラスファイルVerifierは思ったよりおもしろくて、あんな風にスタックに積まれる値の型チェックしてるんだなーと。ただ、あんなのを毎回やってたら確かに起動が重くなりそうな気もする。起動時に-Xverify:noneで起動すると、どこまでVerifyがオフになるかは調べておきたい。

java.util.concurrentはUnsafeに触らなかったので、FutureTask.getの仕組みあたり。
調べたので簡単に書いておく。

FutureTask.getがブロックするのはpark
  • FutureTask.get
  • FutureTask.Sync.innerGet
  • AbstractQueuedSynchronizer.acquireSharedInterruptibly
  • AbstractQueuedSynchronizer.doAcquireInterruptibly
  • AbstractQueuedSynchronizer.parkAndCheckInterrupt
  • LockSupport.park
  • Unsafe.park
FutureTask.getが起きるのはunpark.(以下はExecutoreの場合)
  • ExecutoreがTask.runを呼び出す(例えばThreadPoolExecutor.runWorker)
  • FutureTask.run
  • FutureTask.Sync.innerRun
    • FutureTask.set(結果セット)
      • FutureTask.Sync.innerSet
      • AbstractQueuedSynchronizer.releaseShared
      • AbstractQueuedSynchronizer.doReleaseShared
      • AbstractQueuedSynchronizer.unparkSuccessor
      • LockSupport.unpark
      • Unsafe.unpark
    • FutureTask.cancel
      • FutureTask.Sync.innerCancel
      • AbstractQueuedSynchronizer.releaseShared
      • 後は結果セット時と同じ

SphinxでXeLaTeXを使って日本語使ったPDFを出力する

make latexして作られたMakefileのpdflatexをxelatexにするのと、XeLaTeX用のpreambleをconf.pyに書くだけでいけます。フォントもfontconfig使ってくれるので楽ちん。

conf.pyにpreambleを記述
latex_preamble = '''
\usepackage{xltxtra}
\setmainfont{TakaoMincho}
\setsansfont{TakaoGothic}
\setmonofont{TakaoGothic}
\XeTeXlinebreaklocale "ja"
'''

手順
make latex
cd _build/latex/
sed 's/pdflatex/xelatex/' Makefile > Makefile.xelatex
make -f Makefile.xelatex 

これでPDFが出来てます。

2011年7月18日月曜日

IgoTokenizer直した

ださいバグがあったので、lucene-gosenベースで作っているIgoTokenizerを直しました。
lucene-gosen(trunk/rev.141)ベースになっています。
これで、IgoTokenizerの評価での問題は解決しているはずです。

なお、launchpadからgithubに移動しています。

作業環境のoperaでうまくスクリーンショットがとれなかったので、以前Python Hack-a-thonのハンズオンで作ったPyQt使ってスクリーンショットを取るやつで、スクリーショットを取ってみました(画面から解析するとPOSTになってしまうので、GETで投げてます)

2011年7月17日日曜日

Canvasを使って透過じゃない画像を無理矢理透過して重ねた

muninのグラフを見ていたら、重ねて表示して飛び抜けて変なやつが居ないか確認できたら便利なんじゃないかと思いました。munin側を調べて手を入れてもいいのですが、面倒ですし、どうせブラウザ使って見ているのだからブラウザ上で何とかすればいいと考え、やってみました。

その1. 画像重ねるだけ
globalAlphaを設定してdrawImageするだけです。速い。

その2. グラフだけ重ねる
白(rgb=(255,255,255)のところ)だけalphaを0にして重ねることによってグラフだけが重なって表示された状態にするのですが、当然ながら折れ線グラフじゃないと使えません。
不透明の画像を透明にするためにまず非表示のcanvasにdrawImageして、各画素に対して処理したあと、その結果を表示用のCanvasに重ねて表示しています。遅いです。

一気に全画像を重ねる版
遅いので画像一枚ごとにsetTimeoutする版

2011年7月13日水曜日

launchpadからgithubへ

gist用にgithubのアカウントを作ったので、ついでにlaunchpadにある幾つかプロジェクトをgithubに移してみました。
以下はそのときのメモです。

普段使っているのがbzrなのでbzr側から調べてやりましたが、git-bzrというのがあるようなのでこっちの方が楽なのかもしれません。

必要なもの
  • インポート元となるlpのブランチ
  • bzr, bzr fast-import, (必要ならbzr-git)
  • git
  • インポート先となるgithubのリポジトリ

手順
  1. lpのブランチからブランチしたローカルブランチにcdする
  2. git init
  3. bzr fast-export . | git fast-import
  4. git reset HEAD
  5. git remote add origin git@github.com:アカウント/リポジトリ.git
  6. git push -u origin master  または  bzr dpush git+ssh://git@github.com/アカウント/リポジトリ.git
  7. (もう不要なら) rm -r .bzr

2011年7月3日日曜日

GroovyFXを使ってみる

GroovyFXを使ってみました。題材は前回のカスタムブラウザです。

GroovyFXはリポジトリが公開されているものの、まだまとまったものは配布されていませんので、チェックアウトしてビルドする必要があります。手順はREADME.txtに従えばいいだけなので省略。7/1ぐらいの状態では存在していないクラスを参照していて、そこを外さないとビルドが通りませんでしたが、7/3現在そのままビルドが通ります。

直接JavaFX 2.0 + Groovyで扱うよりはだいぶすっきりしました。今のところ不便なのは、WebEngineloadTaskが特別扱いされていないので、ボタンなどのようにクロージャをそのまま渡せないのと、アニメーションのTransitionの扱いがない(TimelineはBuilderがある)のが不便というぐらいです。
loadTask関係でクロージャを渡せない方は、他のEventHandlerを使っている部分のソースを参考に、ClosureEventHandlerを利用してGroovyっぽくしてみました。
(追記 後からvisibleにしないとサイズ指定が期待通りにならないので変更しました。Window.sizeToSceneを呼ぶのでもいいようです)

2011年6月30日木曜日

JavaFX 2.0をGroovyで使ってみる

JavaFX ユーザグループ 第 5 回勉強会に参加しました。

そこで作ったカスタムブラウザ(参考 カスタムブラウザを作る - Everyday JavaFX)をGroovyで書き直してみました。

使ったJavaFXはJava FX 2.0 Beta (Build 32)です。
Groovyは1.8.0です。



思ったより面倒だったので、次はGroovyFXを使って書いてみようと思います。

2011年6月19日日曜日

emscriptenを使ってみる

LLVMのアセンブリ(.ll)からJavaScriptを生成するEmscriptenを使ってみました。
きっかけは、ちょっとした思いつき

試したかったのは、JS以外の言語でJSの世界に触るコードを書くというものなのですが、JS世界のオブジェクトやら何やらを定義するのが面倒くさくて断念しました。
JSの世界とは全く別のコードをJSに持って行くのには便利だと思います。


以下は、試した手順のメモ。
前提として、LLVM2.9, Clang, v8, Python2.x(2.7.2でやった)は入っているものとします。

1. emscriptenを使えるようにする

[hideaki@archbox ~]$ git clone git://github.com/kripken/emscripten.git
[hideaki@archbox ~]$ cd emscripten/
[hideaki@archbox emscripten]$ python2 emscripten.py 
Usage省略

一回emscripten.pyを実行すると、~/.emscriptenが作られるので環境に合わせて編集する。
編集したのは

  • EMSCRIPTEN_ROOT(~でgit cloneしたので、~/emscripten)
  • LLVM_ROOT(パッケージで入れたので/usr/bin)
  • LLVM_GCC(clang++にした)
  • COMPILER_OPTS(64bit環境なので)
  • V8_ENGINE(パッケージなので/usr/bin/d8)

2. Cのコードを書く

とりあえず、環境が既にあるCにしました。もともと型チェックが欲しいのがきっかけなので、存在しないJSの関数もプロトタイプ宣言します。今回はalertで(後で分かったことですが、alertに文字列を渡しているつもりだったのですが、うまく動きませんでした。当然ながらアドレス(相当)を渡しているので、結果として整数として扱われるようです)

[hideaki@archbox ~]$ cat test.c
#include <stdio.h>
extern void alert(char *msg);

int test(int x, int y) {
    char s[1024];
    int r = x + y;
    snprintf(s, sizeof(s), "%d+%d=%d", x, y, r);
    alert(s);
    return r;
}

int main(int argc, char* argv[]) {
    test(1, 2);
    return 0;
}

3. コンパイルとLLVMアセンブリ作成

[hideaki@archbox ~]$ clang -c -emit-llvm test.c
[hideaki@archbox ~]$ llvm-dis --show-annotations test.o
これでtest.o.llが出来ているはず。

4. JS作成

[hideaki@archbox ~]$ cd emscripten/
[hideaki@archbox emscripten]$ python2 emscripten.py ../test.o.ll > test.js

5. 実行してみる

[hideaki@archbox emscripten]$ d8 test.js
test.js:718: ReferenceError: _alert is not defined
    _alert($11);

当然失敗する。

なお、alertの中身をputsとかにしておけば動きます。
[hideaki@archbox ~]$ cat test.c
省略
void alert(char *s) {
 puts(s);
}
省略

コンパイルとか省略

[hideaki@archbox emscripten]$ d8 test.js
1+2=3

6. ブラウザで実行する


alertは_alertとして参照されるので、_alertを定義します。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <script>
      function _alert(arg) {
        alert(arg);
      }
    </script>
    <script src="emscripten/test.js"></script>
    <script>
      alert(_test(1, 2));
    </script>
  </head>
  <body></body>
</html>

このHTMLをブラウザで開くと、3回alert dialogが表示されます。
最初にmainが実行され一度目、次に_test(1, 2)によって二度目、最後に_test(1, 2)の戻り値がalertで表示されます。

このように、他の言語からJSの関数を使うのは面倒な感じでした。他の言語でロジックを書いて、JSから呼ぶとかそんな感じが一番使いやすそうです。

2011年6月4日土曜日

java.util.concurrentの紹介をやった

会社の勉強会でjava.util.concurrentっていうものが在るんだよという程度のプレゼンをやった。

メモリモデルの説明まではやれないので、大まかにAtomic, 並行コレクション、同期オブジェクト,  Executorの説明をしようと思ったのに、Atomic以外はほとんど出来なかった…。そのAtomicも微妙だったけど。とりあえず定番のインクリメントのデモをやった。

作るプログラムにも依るけど、java.util.concurrentをしっかり使うところって、あんまり無いんだろうか…。普通のサーブレットだと通信やらDBの方が遅くなるし。JDK1.4なところとかもいまだにあるしな…

デモのソース(関数オブジェクトが欲しくなる…)

2011年5月30日月曜日

sleep sortをPython 3.2で

いい加減流行も過ぎましたが、sleep sortをpython3.2でやってみました。Barrierを使っているのでPython3.2以降でしか動きません。要素数が多少増えてもBarrierでスタートがそろうので、待ち時間を100で割ってもそれなりに結果が出る感じです(1000で割ると一桁msのあたりで逆転が起きた)

結果の受け取り方がださい気がして気にくわない。全部が終わってからまとめて取り出したかったのだけど、結局n回Queueからgetする形にした。全スレッドをjoinすれば終わったのは分かるけど、結局Queueから取り出す必要があるのでgetでブロックすれば十分。
何が気にくわないかと言えば、n回取り出すってあたりだけど、一回だけ待つ方法だと、nとEvent(かCondition), Semaphore(こいつがnを知ってるわけだけど)を渡す方法ぐらいしか思いつかなかったのであきらめた。

そもそもQueueはiterableじゃないので一発でリスト化も出来ないことが後で分かった。Queueを使うのをやめて、リスト使って排他制御すればいいと思うけどやってない。
Queueのjoinも良さそうだと思ったけど、これはタスクキュー的なイメージで、空になった場合しか使えないのでダメだった。入力と出力のQueueを分ければ、出力Queueに入れたところで、入力Queueのtask_done呼べば良いはず。

以下コード(gist)

2011年4月23日土曜日

Google ChromeとJavaScriptのタイマー

Chrome(dev)でウェブアプリの類を使っていると、別のタブで作業してから、アプリのタブに戻るといつもなら更新されているはずなのに更新されていないことがよく起きます。
(追記 4/27にChrome 11がStableになったので、stableでも影響を受けるようになります)

なんとなく、アクティブなタブ以外はCPU消費を軽減するためにそういうことをやっているというような記事を読んだ記憶があったので、調べてみました。

まず、Firefox5に関しての記事 たくさんタブを開く人にとってFirefox 5は福音となる…かも をみたことを思いだし読み直してみて、Chromeでやっててもおかしくないと思い調査を継続しました。

ちょっと検索して、Getting smoother animated web content while reducing CPUを見つけました。そこには次のような記述がありました。
In the forthcoming Chrome 11 release, we plan to reduce CPU consumption even for pages that are using setTimeout and setInterval. For background tabs, we intend to run each independent timer no more than once per second. 
Chrome devを使っているので、この影響を受けたのは間違いなさそう。いずれ他のブラウザでもそのうち影響が出てきそうです。

これはタイマーの使い方によっては影響の度合いは違いますが、それなりに影響が出てきそうです。
タイマーの数を節約するために、最も短い間隔で発生させたいイベントの間隔とか最大公約数的にイベントを発生させて、そのイベントハンドラ内でイベントディスパッチしたりすることがあります。
これの実現方法に、どのイベントを起こすかの制御にカウンターを使っていると、イベントの発生回数が減り、カウンタの変化が遅くなるため、イベントの発生タイミングが大きくずれることになります。

簡単なテストを書いてみて確認しました。タイマーのイベントハンドラで、イベント発生回数と時間ベースで、表示を更新するというものです。
前面に表示している間はほぼ同じ速さで数字が増えていくのに対して、裏に回すと時間ベースの方はあまり影響を受けないの対して、イベント発生回数によるほうはどんどん遅れていきます。

イベント発生回数による処理
if (++cnt % 5 === 0) {
    c = $('#counter1');
    c.text(parseInt(c.text()) + 1);
}

時間ベースの処理
var now = Date();
if (now > next) {
    c = $('#counter2');
    c.text(parseInt(c.text()) + 1);
    next = now + 1000;
}

Whooshで始める全文検索的なもの(2)

前回作ったのをGAEに乗せました。ファイルのロックがかけられなくてエラーになるので、そこだけ手を加えました(gae_fileindex.py, gae_filestorage.py)。

最初はIgoTokenizerで試していましたが、GAEに載せたらメモリ&時間食い過ぎで毎回インスタンスが停止されてしまうので、TinySegmenterに変えたらだいぶ良くなりました。

まあ、まだ試してないけど、GAEのStorageを使ってインデックスが作れるのでそっちを使った方が良いかと思います。GAEのStorageを使わないで、ファイルベースのインデックスをGAEで使うメリットは、ローカルで作ったインデックスがそのままGAEでも使えることぐらいだと思います。

リクエストを受けて検索するところ
def get(self):
        qs = self.request.get('q')
        q = self.qp.parse(qs)
        h = self.s.search(q, limit=50)
        template = self.env.get_template('main')
        self.response.out.write(template.render(results=h))

表示テンプレート
<span>ヒット総数{{results.estimated_length()}}ぐらい</span>
{#{{results.key_terms('title')}}#}
  <ol>
{% for item in results %}
    <li><a href="{{item['url']}}">{{item['title']}}</a>({{item.score}})
      <ul><li>{{item.highlights('content')}}</li></ul>
    </li>
{% endfor %}
  </ol>

2011年4月17日日曜日

lucene-gosenベースのIgoTokenizerを作成中

地震やら忙しいやらでしばらくWhoosh関係をいじってなかったので、なかなかエンジンがかからず。
そんななか、lucene-gosenの記事を見て、自分でいろいろ作るよりこれに乗った方が間違いなさそうと感じたので、乗ってみました。

結果はこんな感じ。
試した結果

ソース(sf.netで公開しようと思って登録したけど、まだ何にもしてない。パッケージ名にだけ使ってます)
とりあえず作って、NetBeansのプロジェクトごと公開した状態です。


試すには
必要なものをそろえる
  • Solr 3.1.0を展開
  • lucene-gosenの準備(jar作る。辞書は要らないけど)
  • Igoの準備(jar,辞書)
準備
  • IgoTokenizerをチェックアウト
  • libの下に必要なjarを置く(igo, lucene-core, apache-solr-core, lucene-gosen)
  • ant jar
実行
schemaは次のようにフィールド種別を指定する。
<fieldType class="solr.TextField" name="text_ja" positionincrementgap="100">
  <analyzer>
    <tokenizer class="net.sf.igoanalyzer.solr.IgoTokenizerFactory" dicpath="/home/path/to/ipadic">
    </tokenizer>
  </analyzer>
</fieldType>

2011年3月4日金曜日

Whooshで始める全文検索的なもの(1)

pure-Pythonな全文検索ライブラリであるWhooshを使って簡単な全文検索アプリを作ってみるメモ。

とりあえず、クローラーとかは作るのが面倒なので、ローカルファイルを対象とします。
ちょうど良いので、bloggerからエクスポートしたブログの記事を検索してみます(後でGAEに乗せる予定)

エクスポートするとAtom形式ですが、このパースはlxmlでxpathつかってやりました。もっと良い方法があるかも知れないけど。

インデックス作成
def get_index(path, tokenizer):
    schema = Schema(title=TEXT(stored=True, analyzer=tokenizer),
                    url=ID(unique=True, stored=True),
                    content=TEXT(analyzer=tokenizer))
    if exists_in(path):
        return open_dir(path)
    if not os.path.exists(path):
        os.mkdir(path)
    return create_in(path, schema)

tk = IgoTokenizer(dataDir='ipadic', gae=False) | FeatureFilter([u'^助詞,係助詞.*$']) | LowercaseFilter()
ix = get_index(sys.argv[1], tk)
w = ix.writer()
for url, title, content in  atom_read.read(sys.argv[2]):
    print 'add:', url, title
    w.add_document(title=unicode(title), url=unicode(url), content=content)
w.commit()
ix.close()

importは省きましたが、簡単な事が分かると思います。

全体のコードはリポジトリを参照してください。

libproxyのpythonバインディング

proxyの設定がPAC使っていたりして複雑なときlibproxy(のpythonバインディング)は便利です。URLを指定すると使うべきproxyを返してくれます。

どうやって使用すべきproxyを決めているかはHowToのConfig Modulesに書いてありますが、環境変数とかproxy.conf, GNOME等の設定、wpadなんかを見てくれます。

one linerで
$ python -c 'import libproxy; print libproxy.ProxyFactory().getProxies("http://www.google.com/")'
['direct://']
$ env http_proxy='http://example.com/' python -c 'import libproxy; print libproxy.ProxyFactory().getProxies("http://www.google.com/")'
['http://example.com/']

2011年2月27日日曜日

WhooshのTokenizer追加

IgoTokenizerに加えて
  • TinySegmenterTokenizer
  • MeCabTokenizer
を追加しました。

さらに、FeatureFilter(素性フィルタ)の追加と、IgoのTaggerをインデックスに保存しないモードを追加しました。

使い方
tk = WhooshJapaneseTokenizer.IgoTokenizer(igo.Tagger.Tagger('ipadic'))
tk = tk | WhooshJapaneseTokenizer.Filters.FeatureFilter([u'^助詞,係助詞.*$'])

IgoのTaggerをpickleする

MeCabTokenizerを作っているときに、またpickle出来なくてはまった。なので、独自に保存/復帰をするようにした。
そのとき、WhooshでIgoTokenizerを使うとインデックスが思った以上に大きくなる原因にやっと気が付いた。Taggerをインデックスに保存するのが原因。
そこで、IgoのTaggerも独自に保存復帰が出来るようにした。

# coding:utf8
import cPickle,gzip
import WhooshJapaneseTokenizer,igo.Tagger
tk = WhooshJapaneseTokenizer.IgoTokenizer(igo.Tagger.Tagger('ipadic'))
tk2 = WhooshJapaneseTokenizer.IgoTokenizer(dataDir='ipadic', gae=False)

print 'w tagger', len(cPickle.dumps(tk, -1))
print 'wo tagger', len(cPickle.dumps(tk2, -1))
tk = cPickle.loads(cPickle.dumps(tk, -1))
tk2 = cPickle.loads(cPickle.dumps(tk2, -1))

for t in tk(u'今日はいい天気です。'):
  print t.text,
print
for t in tk(u'今日はいい天気です。'):
  print t.text,
print

出力
$ python test2.py 
w tagger 41185761
wo tagger 119
今日 は いい 天気 です 。
今日 は いい 天気 です 。

これを逆手に取れば、Taggerをpickleしておけば、辞書が無くてもTaggerを作ることが出来る。
>>> import igo.Tagger, cPickle, gzip
>>> from cStringIO import StringIO
>>> cPickle.dump(igo.Tagger.Tagger('ipadic'), gzip.GzipFile('/tmp/tagger.gz', 'w'), -1)
>>> cPickle.load(gzip.GzipFile('/tmp/tagger.gz'))
<igo.Tagger.Tagger instance at 0x7f24f71c9a28>
これで大体8.4Mぐらい。

2011年2月21日月曜日

WhooshのTokenizerを作るときにはまったこと

TinySegmenterTokenizerを作っていたときにはまったこと。

WhooshではSchemaがインデックス内にpickle化されて保存されるので、Schemaを作るときに指定したTokenizerもpickle化出来ないとエラーになります。

コンストラクタで
def __init__(self, strip):
    if strip:
        self.strip = unicode.strip
    else:
        def strip(s):
            return s
        self.f = strip
等とやっていてはまりました。

データを作ったTokenizerがセットで保存されているのは良いと思うのですが、インデックスを複数のPythonのバージョンで共有できるのか気になります。そのうち試す…

はまったことではありませんが、pythonのunicode.stripは全角スペースやNBSPなども除去してくれる優れものであることが分かりました。

Whoosh用の日本語トークナイザを追加(TinySegmenterTokenizer)

辞書なしで使えれば手軽だと思ったので、TinySegmenter版も作りました。

PyPI whoosh-igo(パッケージ名とりあえずそのまま)
Launchpad Japanese Tokenizers for whoosh/

モジュール名(IgoTokenizer)がちょっとカバー範囲が広すぎると思ったので、
Tokenizerを追加したついでに、WhooshJapaneseTokenizerに変更しました。
長いけどこれならぶつかる可能性が減ったはず…

2011年2月19日土曜日

Whoosh用の日本語トークナイザ(IgoTokenizer)

Python hack-a-thon 2011.02の成果。

Igoを使って、Whoosh用のTokenizerを作りました。とりあえずですが使えます。
https://launchpad.net/igotokenizer

テストコード
# -*- coding: utf-8 -*-
import os
from whoosh.index import create_in
from whoosh.fields import *
from whoosh.qparser import QueryParser

import igo.Tagger
import IgoTokenizer

tk = IgoTokenizer.IgoTokenizer(igo.Tagger.Tagger('ipadic'))
scm = Schema(title=TEXT(stored=True, analyzer=tk), path=ID(unique=True,stored=True), content=TEXT(analyzer=tk))

def add_docs(w):
    w.add_document(title=u'その1', path=u'1', content=u'こんにちは世界')
    w.add_document(title=u'その2', path=u'2', content=u'さようなら世界')
    w.commit()

def search(s, qp, text):
    print 'search ' + text
    for r in s.search(qp.parse(text)):
        print r['path'], r['title']

if not os.path.exists("indexdir"):
    os.mkdir("indexdir")
ix = create_in('indexdir', scm)
w = ix.writer()
add_docs(w)

s = ix.searcher()
qp = QueryParser("content", schema=ix.schema)

search(s, qp, u"こんにちは世界")
search(s, qp, u"世界")
search(s, qp, u"こんにちは")
search(s, qp, u"さようなら")

実行結果
search こんにちは世界
1 その1
search 世界
1 その1
2 その2
search こんにちは
1 その1
search さようなら
2 その2

2011年2月14日月曜日

Compose key in X11(Debian GNU/Linux)

Debianといいながらaptosidだが…

VMのOSを64bit化するため、新しいVMをつくってaptosid-xfceの64bit版を入れた。
作業は、xfceからlxdeへの変更と、一通りパッケージを入れたところで、古いVMのhomeをコピーするだけで終了。
ついでにbtrfsにしようかと思ったけど、インストーラーで選べなかったのでやめた。


で、今回の本題。前は特にいじった記憶がないのだが、Windows KeyがSuper_Lとして使えなくなり、awesomeが(そのままでは)使い物にならなくなった。もしかしたら、前回はsidux-kdeからスタートしたからその影響かも知れない…

xevでみると、keycode=133で正しいコードが上がってくるけど、multi_keyになっていた。Xのログを見ると設定してないのに、xkboptionsが設定されているので、設定値から検索すると/etc/default/keyboardで設定されていた。

XKBOPTIONS="lv3:ralt_switch,compose:lwin"

確かにleft windows keyがcomposeキーになる設定になっている。前の設定を見ると

XKBOPTIONS="compose:rctrl"

だった。ただ、rctrlはVMのHostキーに割り当てているのでcomposeキーがなんだか分からないけど、なんか気分が良くないので、menu keyに割り当てるようにした。

XKBOPTIONS="compose:menu"

これでwindowsキーがSuper_Lになって、awesomeでもlxde(Win-R,Win-Eとか)でも問題なくなった。

ついでにcompose keyについて調べたら、使い道が分かった。綴り字記号とかで使うものらしい。
compose + ' + a = á
compose + " + a = ä
compose + ` + a = à
compose + , + c = ç
compose + ' + e = é
compose + ^ + e + ê
compose +` + e = è
compose + ? + ? = ¿

2011年1月23日日曜日

NetBeans 6.9.1でJavaScriptを実行する

RhinoはJavaのクラスを使う必要はあるが、ちょっとしたツールを作るとか、いろいろいじって確認したいといった用途なら、コンパイルも要らないのでとても手軽。JDK6に含まれているので、JDK6が入っていれば他に何も要らないのが特に有利です。

以前はNetBeansでJavaScriptを編集して、そのままRhinoで実行できた記憶があったのがだが、ちょっと調べた限り6.9.1では出来なかった。

ただ、とりあえず実行するだけなら簡単で、jrunscriptのMainを呼ぶだけでいい。Javaのクラスとかも補完してくれると良いんだけど…


手順は簡単

  1. 適当なJavaプロジェクトを作る
  2. JavaScriptのファイルを追加して適当に作る
  3. プロジェクトの実行ライブラリに${JDK_HOME}/lib/tools.jarを追加
  4. 実行の主クラスにcom.sun.tools.script.shell.Mainを設定
  5. 引数に2.で作ったJavaScriptのファイルを指定(JavaScriptに引数が必要ならさらに続けて書く。この引数はargumentsと言う名前で参照できる)
  6. 実行
これだけ。ついでに、jarの作成をしないとか、src/testフォルダを削除すればいい。

ソース,テストパッケージフォルダは不要

実行用ライブラリにtools.jarを追加する

jarは作らない

主クラス変更, 実行するjsファイルとjsに対する引数を書く

実行してみた

2011年1月13日木曜日

はてなのキーワードリンクを削除するブックマークレット再び

はてなのキーワードリンクを削除するブックマークレットで除去されないキーワードがあったので見たら、どうもokeywordというクラスもあるらしいことが分かりました。
そこで、keyword, okeywordのリンクを除去するようにしてみました。Selectors APIを使って、両方いっぺんに検索するようにしました。IE8ではドキュメントモードをIE8標準にすれば動きました。

javascript:(function(){var d=document,a=d.querySelectorAll(".keyword,.okeyword"),v,n=a.length;while(n--){v=a[n];v.parentNode.replaceChild(d.createTextNode(v.innerHTML), v)}})()

しかし、Selectors APIは便利ですね。


2011/2/13 追記
上のbookmarkletでは検索語がハイライトされている場合(アンカー中にハイライト用spanが入っている)に期待通り動かないので、対策しました。
自分でNodeを辿るしかないかと思っていましたが面倒だと悩んでいたところ、ふとDOM Rangeを思い出したので調べ直してみたらこれが便利そうなので使ってみました。かなり簡単で便利です。

ついでに検索語のハイライトも無効にするようにしてみました。はてなの検索語ハイライトはキャッシュが効いてしまって、検索してないのにハイライトされることがあって微妙だと思っていたので…

javascript:(function(){var d=document,r=d.createRange(),a=d.querySelectorAll("span.highlight,a.keyword,a.okeyword"),n=a.length,v;while(n--){v=a[n];r.selectNodeContents(v);v.parentNode.replaceChild(r.extractContents(),v);}})();
span.highlightを削れば、検索語のハイライトは残ります。

PythonでのU+005CとU+00A5

U+00A5(¥)は円記号, U+005C(\)はバックスラッシュで,
SJISにしたとき問題になる可能性があることはよく知られている.

Python(2.6.6)でのこの挙動を確認してみた.

>>> print u'\u00a5'
¥
>>> print u'\u005c'
\
>>> print u'\u005c'.encode('sjis')
\
>>> print u'\u00a5'.encode('sjis')
\
>>> u'\u00a5'.encode('sjis') == u'\u005c'.encode('sjis')
True

CP932を使うことが多いので, そっちでも確認しようとしたらU+00A5は変換できなかった.
>>> print u'\u005c'.encode('cp932')
\
>>> print u'\u00a5'.encode('cp932')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'cp932' codec can't encode character u'\xa5' in position 0: illegal multibyte sequence