その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

トークナイザーとパーザーについて,結合するということについて

トークナイザーとパーザーについて,それに準ずる物を書いていて,その最中ふと思った事について記す.

基本的にトークナイザーは対象となる文字列を,或る最小限の意味を持つトークンに分解してトークン列を導出するというのが責務で,これのみに着目するのであれば実装は (それほどまで) 難しいものにはならない *1
厄介なのはパーザーの方で,パーザーはトークナイザーが吐き出したトークンを組み合わせてある意味のある文節というか文脈というか,そういう感じの扱うのに際して便利な単位に落としこむのが仕事なのだけれど,こちらの方は様々な組み合わせが存在するために複雑になる.伴ってコードの量も多くなる.圧倒的にトークナイザーを書くよりも実装のコストが高い.
そうした状況を解決する為に,愚鈍なトークナイザーを賢いトークナイザーにしてしまうという方法が考えられる.トークナイザーが単純な文字列の分解だけではなく,その過程で附随的な情報をトークンに付け加えたり,或いはトークナイザーのレベルでいくつかのトークンをひとまとめにしてしまうというような「賢い」処理を行い,トークナイザーの成果物をパーザーフレンドリーにすることで,パーザーの実装コストを緩和するという方法だ.
これは大抵の場合,トークナイザーとパーザーが完全に独立している時の総合コストよりも低くなる感じがするんだけど,そうなるとトークナイザーとパーザーが結合を始めてしまい,トークナイザーを別の所で使ったり,或いは逆でパーザーのトークナイズエンジンを変えたりすることがままならなくなる.
と,ここらへんトレードオフだと思っていて,まあよく考えたほうが良いですよねという話で,実際誰も使わないようなトークナイザー・パーザーだったら密結合させてエイヤで実装したほうが良いと思う.逆に超有用 (だと思われるよう) なものの場合はやっぱり双方独立させたほうが良いのだろうとも思うけど,そこまで有用なものなのだったら書かずとも既にこの世に存在している可能性が高いし,もしかしたら密結合でソイヤと実装してしまってもどこからともなく神コミッターが現れて一晩で結合をひっぺがしてくれるかも知れないのでとにかく書いてしまうというのが重要である.

*1:例外はあると思う

Excel::Writer::XLSXのset_optimization()について

基本的にExcel::Writer::XLSX使うときは

my $workbook = Excel::Writer::XLSX->new('yabai.xlsx');
my $worksheet = $workbook->add_worksheet();
...

みたいな感じで使うと思うんですが,素朴にこれでやっていこうとするとExcelの行数が増えた時にメモリをバカ食いしてしまって,最悪の場合死に至ります.実際さっき死んだ.

そこでどうするかというと,set_optimization()というメソッドadd_worksheet()する前に呼んでやると良いです.

my $workbook = Excel::Writer::XLSX->new('yabai.xlsx');
$workbook->set_optimization();
my $worksheet = $workbook->add_worksheet();
...

PODにも

The "set_optimization()" method is used to turn on optimizations in the
Excel::Writer::XLSX module. Currently there is only one optimization
available and that is to reduce memory usage.

とあるように,現在のところ使用メモリ量を削減するための手立てはこれしか無いので使うほかないです.

トレードオフとしては,「既にセルに書き込まれているデータについて操作するいくつかの処理が動かない」というのがあって,これはぶっちゃけテーブル関係の処理が動かなくなるという意味なんですが,実際のところPerl経由でテーブル関係の処理とか滅多に使わないし,万が一使う事になってもそれはその時になってから考えればよろしいので,とりあえず何も考えずにset_optimization()を呼んでおけば良いという認識です.行数が多い時は速度も速くなるようですし

とにかく重要なのは,ドキュメントにあるようにadd_worksheet()する前にset_optimization()しなければならないということです.逆だと最適化が働かないので.

JavaScriptで\x特殊文字と同等の事を\xを使わずに実現する

JavaScriptの文字列中では\xという特殊文字が利用できます.ドキュメントによると,

\xXX 00 から FF までの 2 桁の 16 進数 XX で指定された、Latin-1 エンコーディングの文字。例えば、\xA9 は著作権記号を示します。

とのことで,まあここに書いてある通りの動作をします.

例:

"\xa9";
// => "©" 


ところでこの\xは文字列連結等では利用できない,つまり

"\x" + "a9";
// => ERROR!

という風には使えないわけです.そりゃそうだ.
どっこい変数の中身を\xに適用したい時などに困るわけですね.


そういう時は以下のようにすることで代用が可能です.

var charCode = "0x" + "a9"; // <= これは文字列
String.fromCharCode(parseInt(charCode, 16));

0xから始まる16進数の文字コードを表す文字列をparseInt()に与えて10進数の文字コード数値に変換し,更にそれをString.fromCharCode()に食わせる事で,所望の文字を得ることが出来るという感じです.


[追記] parseInt()の基底を16にしておけば"0x"をプレフィックスで付ける必要は無かった.

var charCode = "a9"; // <= これは文字列
String.fromCharCode(parseInt(charCode, 16));

16進数の文字コードを表す文字列 ("00"〜"ff") を基底16進のparseInt()に与えて10進数の文字コード数値に変換し,更にそれをString.fromCharCode()に食わせる事で,所望の文字を得ることが出来るという感じです.


無駄に詰まったのでここに記す.

minifyされたJSON文字列をJavaScriptで整形する

var indentation = "    ";
JSON.stringify(JSON.parse('{"foo": [1, 2], "bar": {"buz": "quz"}}'), null, indentation);

みたいに書くと,

{
    "foo": [
        1,
        2
    ],
    "bar": {
        "buz": "quz"
    }
}

という風な整形された文字列が返って来て便利だった

Perl Casual #06に行ってきた

行ってきたついでに話してきました.


とりあえず先日Perl::Lintが出来たので,その一連の開発で得られた知見に就いてシェアーさせて頂きました.大体スライドに書いてある事をしゃべりました.


発表時は軽い感じで流したんですけど,3番目の「誰でも開発に参加できるようにしておく」というのは割と重要な話題だと思っています.
OSSだとそのライブラリやプロダクトを「利用する為の」ドキュメントは提供されていることは多いですが,「開発の手引きとなるような」ドキュメントはあまり提供されていないように感じていて,そういうのが無いと手がかりなしでソースコードを読む羽目になるので,開発参加に際しての障壁が高くなるのではないかと思います.そういった開発の手引き (ドキュメントであれチュートリアルであれ) や,「ここのソースコード読めば大体雰囲気わかるよ」的な足がかりになる部分へのポインターを提供するのは,協調作業をする (したい) 上で重要なのでは無いかと思った次第です.
また「誰でもやれる」と言うのも大切で,新機能を実装したいときにその為のスケルトンを生成してくれるジェネレータスクリプトや,リリースの自動化スクリプトなども提供しておくことで,ある程度規律を保った上で属人的ではなくなる,いわゆる「お願いします脳」みたいなものも排除できて,作業効率や開発への参加しやすさが向上するような気がしています *1

ここらへんは今年のYAPCAbigailさんのReleasing perlの発表や,懇親会でid:shiba_yu36さんと話してて気づきを得た点です.感謝.


そしてとにかくPerl::Lintの開発はつらかった.現場からは以上です.

*1:最初のほうでOSSと言いましたが,普通に会社とかのプロジェクトでも同様だとは思います

今週金曜日はテンプレートエンジンNightです

今週金曜日 (10月12日) はテンプレートエンジンNightです!!


テンプレートエンジンNight on Zusaar

多くのテンプレートエンジン関連の発表が目白押しとなっております.
各位におかれましてはふるってご参加いただきたく存じます.

参加可能枠を拡張いたしましたので,「人が多すぎて参加できなさそうだワ!」という方でもご参加頂けるようになったのでは無いかと思います!!!


それでは当日お会いしましょう!

PHPでお手軽にベンチマークを取るためのツール BenchMarker を書いた

PerlにはBenchmark.pmというお手軽にベンチマークを取るためのライブラリがあって,これがまあ便利なわけです.
そこで,深い事情により最近書いているPHPにもこういうツールがあると便利だろうなーってことで書いたのがこのBenchMarkerになります.

moznion/BenchMarker · GitHub
moznion/benchmarker - Packagist

ノリと勢いでPackagistにもアップロードしましたのでお手軽にご利用いただけます.


使い方ですが,cmpThese()を例に取ると,

<?php
$code = function () use ($foo) {
    // do something
};

$benchmarker = new \Moznion\Benchmarker();
$benchmarker->cmpThese(10000, [
    "code A" => function () {
        // do something
    },
    "code B" => $code, // callableな変数も渡せる
]);

という風に,無名関数を渡してやると,

           Rate  code A  code B
code A  22571/s      --    -49%
code B  44656/s     98%      --

という感じで,各々のコードを10000回試行した比較結果がわかりやすい出力されて便利!!! という勢いです.


ほぼほぼPerlのBenchmark.pmからパクってきたので,知ってる人ならサクッと使えるのではないかという感じです.
詳しくはBenchMarkerのドキュメントかBenchmark.pmのドキュメントなんかを参照してくださいませ.