Perl Advent Calendar Japan 2011 Casual Track 4日目に Groonga Casual Tutorial を POSTしました。

Perl Advent Calendar Japan 2011 Casual Track 4日目でGroongaを使った簡易チュートリアルを投稿しました。

groongaをmysql経由で使うのではなく、groongaをhttpサーバーとして起動しておいてhttpリクエストでやり取りをする方式での開発方法を紹介しています。

このチュートリアルを実際に実行してくれる人がいたらうれしいです。

Plack::Builder+Plack::Middleware::Staticで Plack::App::Directoryみたいな事をやってみる。

そんなわけでとりとめもない記事です。

ディレクトリ構成

こんな感じ

static.psgi
root/
root/index.html
root/xxx.html
root/hoge/index.html
root/hoge/xxx.html

psgi

% cat static.psgi
use Plack::Builder;builder {
enable "Plack::Middleware::Static",
    path => sub { s!(.*/$)!${1}/index.html! or return qr{^/.+} },
    root => './root/';
};

結果

解説

Plack::Middleware::Staticの_handle_static()を見てみます。

package Plack::Middleware::Static;
use strict;
use warnings;
use parent qw/Plack::Middleware/;
use Plack::App::File;

use Plack::Util::Accessor qw( path root encoding pass_through );

...

sub _handle_static {
    my($self, $env) = @_; 

    my $path_match = $self->path or return;
    my $path = $env->{PATH_INFO};

    for ($path) {
        my $matched = 'CODE' eq ref $path_match ? $path_match->($_) : $_ =~ $path_match;
        return unless $matched;
    }   

    $self->{file} ||= Plack::App::File->new({ root => $self->root || '.', encoding => $self->encoding }); 
    local $env->{PATH_INFO} = $path; # rewrite PATH
    return $self->{file}->call($env);
}

以下の行でAccessorとしてpath, rootと他2つ定義されています。

use Plack::Util::Accessor qw( path root encoding pass_through );

そのpathですが、以下の記述からコードリファレンス、もしくは正規表現のリファレンスを期待している事がわかります。

my $matched = 'CODE' eq ref $path_match ? $path_match->($_) : $_ =~ $path_match;

ここで自前で実装したstatic.psgiでpathに対して次のようなコードリファレンスを渡します。これはこの無名関数に渡された文字列の最後が/で終わる場合、その部分を/index.htmlと書き換えます。

sub { s!(.*/$)!${1}/index.html! or return qr{^/.+} }

もし、書き換えられた場合は次の行でrewriteされます。localはそのスコープ内でのみ値を一時的に保存します。

local $env->{PATH_INFO} = $path; # rewrite PATH

localで一時的に保存された値はスコープ内でのみ有効。で、そのスコープ内でPlack::App::File->new->call($env)が実行されます。

$self->{file} ||= Plack::App::File->new({ root => $self->root || '.', encoding => $self->encoding }); 
local $env->{PATH_INFO} = $path; # rewrite PATH
return $self->{file}->call($env);

余談

ちなみになんとなくこの処理はこの順番のほうが(処理の流れを説明する場合は)いい気がする。次のような感じ。

local $env->{PATH_INFO} = $path; # rewrite PATH

$self->{file} ||= Plack::App::File->new({ root => $self->root || '.', encoding => $self->encoding }); 
return $self->{file}->call($env);

でもlocalで変化している期間を最小にしたいなら元の書き方が正しい気もする。うーん。

おわりに

ちなみにこの正規表現では'ディレクトリの最後は/'という前提のものです。
おしまい。

独り言

はてなぶろぐつかってみたいなー

hachiojipm#10に行ってきた

場所

私は2度目の南米ペルー料理でした。
1度目と違ってビールとかチョリソとかお肉とかパスタとかタコの刺身とかがつがつして食ってました。

もうすでにhachiojipmの常連?

LT

1.norry_gogo さん
  • 最強の学習法について。「試して失敗するという過程」は無駄ではないと。

2.hondallica さん
3.vkgtaro さん
  • どっかで聞いたことある名前だなーと思ったら海賊たろーさんでした。
  • 昔Config::Multiを狂ったように使ってましたよ。
4.yellow844 さん
  • エ○タ○ブ○イ○ってとこで働いていたらしく、奇遇ですね、僕もです。的な。
  • 待遇が恐ろしく違っていたので「あの頃僕は力のある大人達に守られていたんだなあ」と思いました。どこに属するかで運命ってすごくかわるんですね。
  • 発表内容はブラックな人々のお話でした。あるSEさんが言い放った「AJAXつかえませんよ。Tomcatサーバーにいれてませんから。」は伝説です。
5.usayman さん
  • 久しぶりにいらっしゃったらしく、私とは初めまして。発表内容は「職場環境のあるある、ないない」
  • そうじのおばちゃんやヤクルトのおばちゃんは思ったよりも権力があったりしますよね。
  • おばちゃんがさいきょうって事でいいんじゃないかな。

6.me
  • ぼくがかんがえたさいきょうのアレ。後述。

7.norie さん
  • オーケストラを趣味でやってるそうです。なんかぐるぐるしてるやつを担当しているそうです。
  • 発表内容は最強の関数。ぼくはよっばらっていたのでかんすうはよめませんでした。

8.uzulla さん
  • ATND的なイベントツールを作りたい。という構想を発表してました。
  • イベントに来た以上ひとりで終わるのを防ぐ仕組みを、というのはhachiojipmに来ている全員が同意。

9.una さん
  • どちらかというとデザイナさん。エンジニアの会話面白いらしい。
  • 技術ばっかの話じゃないからねぇ。みなさんも気軽におこしやす。

10.ktat さん
  • 自己紹介されるまで以前面接行った会社のCTOと気づきませんでした。その節はどうも。
  • 発表内容は設定ファイルでWEBアプリケーションのテストをしましょうというツールです。
  • ログイン、ログアウトの処理もできるそうです。

11.hsksyuskさん
12. kyannyさん
  • はじめまして。こんにちは。なんとなく場慣れしているので誰だろうとずっと思ってましたが
  • 帰ってブログ見て刺身ブーラランさんだと気づきました。よくよく考えればすごい名前。
  • REMPというクロム拡張ツールを紹介。youtubeの動画を登録して自動再生。なんなら他人と共有。
  • https://chrome.google.com/webstore/detail/cdmjnmpmcgfkkdjjdgpkikbgnnojbmbo?hl=ja
  • yellow844さんが「弊社と競合している…」とか言ってたような。
13. ytnobodyさん
  • YAPCではリポーターお疲れさまでした。実際にやってみた感想を発表。
  • hirataraさんは最強リポーターとの事。一緒に働いた人がそう言うなら間違いない。
14. hide_o_55さん
  • node学園経由のため若干遅刻してました。ライアン(だっけ?)のライブコーディングがパねぇらしいです。
  • ぼくのかんがえたさいきょうのモジュール。「例外を投げたいだと?投げられるのはてめぇーだ!!」
  • ちなみに帰りの電車で技術的な会話してましたが、とても高度な事ばっかし言うのでなんとなく投げられた気分です。
15. hirobanexさん
  • エアhachiojipm常連のhirobanexさんが此度のpmにはいらして下さいました。
  • LT後に各地でO/Rマッパ宗教論が勃発してました。
16.makamaka2_donzokoさん
  • お約束の若干遅刻にて登場。LTが始まる前に急いで血中濃度を上げないといけないので大変です。
  • これまでで最も強度な材質(通称段ボール)でのlT。もう彼には普通のLTが許されないのかもしれない。。。

私の発表

groongaをもっと手軽に使えるようにしておけばgroonga使う人増えると思ったのでgroongaを意識しなくてもgroongaの恩恵を受けることができるアプリを作ってみようかと思いました。

http://okamuuu.github.com/Gang/presen/index.html
https://github.com/okamuuu/Gangoka

実はGroongaはそもそもWEB管理画面をデフォルトで装備しているのでCRUDを実装する必要あるのかだとか、WEBAPIのエンドポイントも最初からあるのでは、という話ではあるんですが
若干使いづらい箇所がいくつかあったのでBlogに特化したインターフェースにしたいなと思った次第です(まだそうなってませんが)

今後リファクタリングして完成度を上げてから、どこかで再度発表する予定です。

しばらくプロセスきらずにいるので自由にお試し下さい。
http://106.187.44.245/article/list

感想

hachiojipmではお酒を一緒に飲む仲間ができました。今後この活動が継続できる事を願ってます。

提案

いつも同じメンバーがMac持ってきてくれていますが(私、持ってきてません)、
そもそも水をかぶるリスクを彼らのみが負っているのでいざという時の保険というか一人500円とか保険積み立てするとか
そういう仕組みがあればいいかも。

まとめ

hachiojipmサイコー。今度町田でもやるよ。

フィボナッチ数をPerlで実装できますか?

フィボナッチ数をPerlで実装できますか?

そもそもフィボナッチ数が何なのかまったくわからなかった。くやしいです。
くやしいので勉強してみた。

コード置き場

ここにコードを置いてあります。
https://github.com/okamuuu/Fibonacci

実装前に準備すること

ケルトン作成

% module-setup Fibonacci                                   
...
% cd Fibonacci

テスト作成。

#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;

BEGIN {
    use_ok('Fibonacci');
};

my $expected = [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 ];

subtest 'get fibonacci number' => sub {

    my $got = Fibonacci->get_number_at(4);
    is($got, $expected->[4]);
};

done_testing;

実行する。もちろん失敗。

:!prove -vl t/01_fibonacci.t                                               
t/01_fibonacci.t .. 
ok 1 - use Fibonacci;
Can't locate object method "get_number_at" via package "Fibonacci" at t/01_fibonacci.t line 14.
    # Child (get fibonacci number) exited without calling finalize()
not ok 2 - get fibonacci number
...

実装開始

フィボナッチ数の定義はどの項もその前の2つの項の和となっている。という事なのでベタに次のように書いてみる

package Fibonacci;
use strict;
use warnings;
our $VERSION = '0.01';
use Carp ();

sub get_number_at {
    my ($class, $length) = @_;

    if ( $length == 0 ) {
        return 0;
    }

    ### 1つ目、2つ目の数はその2つ前の値が存在しない
    if ( $length == 1 or $length == 2 )  {
        return 1;
    }   

    my @numbers;

    for my $len ( 1 .. $length ) { 
        if ( $len == 1 ) { 
            $numbers[$len] = 1;
        }   
        elsif ( $len == 2 ) { 
            $numbers[$len] = 1;
        }   
        else {
            $numbers[$len] = $numbers[$len-1] + $numbers[$len-2]; 
        }   
    }   

    return $numbers[$length-1] + $numbers[$length-2];
}

1;

テスト実行。成功。

:!prove -vl t/01_fibonacci.t
...
All tests successful.
};

done_testing;

テストケースを増やしてみて再度prove実行。成功。

#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;

BEGIN {
    use_ok('Fibonacci');
};

my $expected = [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 ];

subtest 'get fibonacci number' => sub {

    my $count = scalar @{ $expected } - 1;
    for my $length ( 0 .. $count ) { 
        my $got = Fibonacci->get_number_at($length);
        is($got, $expected->[$length]);
    }  
};

done_testing;
:!prove -vl t/01_fibonacci.t
...
All tests successful.
Files=1, Tests=2,  0 wallclock secs ( 0.05 usr  0.01 sys +  0.04 cusr  0.00 csys =  0.10 CPU)
Result: PASS

面接官が試したかった事。

Fibonacci.pmのget_number_atメソッドの最後にあるこの記述。再帰の匂いがぷんぷんします。

return $numbers[$length-1] + $numbers[$length-2];

ああなるほど面接官が知りたかったのは「再帰処理を書けるのかい?んん?」とそういうわけですね。承知した。

再帰処理に書き換える

lib/Fibonacci.pmを次のように書き換えます。
うおー。シンプル!!

package Fibonacci;
use strict;
use warnings;
our $VERSION = '0.01';

sub get_number_at {
    my ($class, $length) = @_; 

    if ( $length == 0 ) { 
        return 0; 
    }   

    if ( $length == 1 or $length == 2 ) { 
        return 1;
    }   

    return $class->get_number_at($length-1) + $class->get_number_at($length-2);
}

1;

テスト実行。当然成功。

:!prove -vl t/01_fibonacci.t                                               
t/01_fibonacci.t .. 
..
All tests successful.

感想

再帰処理は見た目何が何やら分からないかも知れませんが、どっかにwarnでも挿入して実行してみると何やってるか良く分かります。
小一時間程度でしたが割といい勉強になりました。おしまい。

Hachioji.pm #8

0からはじまってるので今回が9回目の開催となるhachiojipmに行って参りました。

ごはん

今回は沖縄料理屋さんでした。ゴーやチャンプル美味しかった。

おしゃべり

今回もPerlやらPHPやらJQuery-MobileやらGANCやら、いつもどおり幅広い話をしておりました。

LT

自己紹介を兼ねたLT開始。テーマは「夏の自由研究」です。

[twitter:@hide_o_55]さん

Github Growlerを参考にしてMetaCPAN Glrowlerなるものを作成したそうです。github.comにコードがあるので拝見。
コードも短いしData::MessagePackとCache::LRUを使ってるのでこのモジュールを知らない人は自分のPCで動かしてみると勉強になるんじゃないかと。

[twitter:@uzulla]さん

サーバーをKillするApache killerを実験してみたという話

  • 引数が渡せない(バグ)
  • 攻撃対象となるindex.htmlが小さすぎて攻撃が成立しない。

Apache Killerは使用メモリを増やすためにRangeヘッダに余計なパラメータをつける事によって発動するようです。

できればApacheじゃなくてnginx使えば良いのですが、組織の都合もあるでしょうからApache使ってる人は対策しておきましょう。

[twitter:@k_hisa]さん

ATNDにも記述しておりましたがどうやらご近所さんらしく、今回初参加、おいでやす。
アフィリエイト関連の部署で、最近はActionScriptをやってるそうです。

八王子とコラボするなにかを作りたいそうです。仕事以外ではiPhoneアプリに興味あるという事で試作したiPhone向けのアフィリエイト商品検索アプリをデモしてくれました。

[twitter:@mgiken]さん

おなじみのGANCの最新情報を提供して下さいました。最近GANCパートナーが一人見つかったとの事。
ちなみにGANCを他のコミュニティで話をしても受け入れられない、ここまで受け入れてくれるのはhachiojipmだけだそうですw

[twitter:@umeyuki]さん

残念ながら所用の為LT前に帰路につかれました、のでuzulaさんが代理で資料を読み上げます。

「名刺にグルメなプログラマって書いてたら、Android開発手伝うとこになった」

やったもの勝ち、言ったもの勝ちの世界ではしっかりとPRする事が大切だなーという深いお話ですな。

[twitter:@hisaichi5518]さん

夏休みにインターン生として東京方面においでになって初参加、おいでやす。
インターン先の社名を聞いてちょっとうらやましい。

自作WaFを作成していたそうですが、インターン先での意見をとりいれて新たなWaFを作っているそうです。

[twitter:@norry_gogo]さん

Amon2でTwitterクライアント作成に挑戦中との事。
ひとまずはAuthでのログイン、ログアウトを作ったところで色々な発見がありました、との事。

あんまりPerlのコードレビューをする機会がないのでhahiojipmはありがたいようです。

[twitter:@ono_pm]さん

お初にお目にかかりました。こんにちは。Solarisを使ってるそうです。昼間にOpenSolaris 勉強会に行ってから、夜hachiojipmに来たっていう、お疲れさまです。

暑いので室温はCloudForecastで監視してPlackで作ったWebアプリにリクエスト投げてエアコンをon/offするアプリをデモ。
記録から帰宅時間がバレバレっていう。

俺のターン

集合知プログラミング第2章を要約した話をするつもりでしたがなんかグダグダ。
Three.jsを使ってある人々の嗜好をビジュアル化して、傾きが近ければ似ている、っていう説明をすればわかりやすいかなーと思いました。

[twitter:@maka2_donzoko]さん

Perl応援歌をつくってきましたw
まあとある曲の替え歌で、歌詞とか面白かったんですが、肝心の曲をだれも知らなかったっていう...

勝手なイメージですけど、ラリー・ウォールならきっと絶賛したんじゃないかな。

Air LT

[twitter:@ytnobody]さん

夏休みなので蝉をつかまえました。

みーんみんみんみんみんみんじーーーーーーーーーw (音)

音量注意

[twitter:@hirobanex]さん

TimeLine.pmに共通化された処理が書かれてないからちょっと分かりづらいかも?

次回の開催予定

次回は10/1(土)に開催予定です。10月はPerlで忙しい月になりそうです。
hachiojipmのLTはlaughing Talkです。お気軽におこしやす。

DBチューニングできますか?(6)

概要

というわけで最近いろいろと面接行ったりしてます。

ルールとコスト

面接官「DBチューニングできますか?」
私「そうですねー(中略)『インデクスとクエリからオプティマイザが何しようとするか程度は意識してます。』」

この後面接官はこう言いました。

面接官「あ、じゃあルールベースオプティマイザとコストベースオプティマイザの違いを説明してもらえる?」

しどろもどろに説明する

とても疲れましたが何とか説明できた(ような)気がしています。

とはいえもう一度同じ事聞かれるとつらいので流暢に答える練習をしておきたいのです。

ルールベースオプティマイザ

たとばこんなクエリがあるとします。mySQLinnoDBだとしてAとBはともにインデックスが生成されているものとします。

select * from TABLE
where A = '10' and B like '123%';

この場合どのようにデータを取得すると一番効率が良いのか?を判断する必要があるのですが、
その方法の一つがルールベースオプティマイザです。

上記の場合、=とlikeのどちらが優先順位が高いかという判断が必要なのですが、この場合'='だと仮定します。おそらくオプティマイザは

「最初にA='10'なインデックスファイルを読み込もう。そうするとそのレコードのプライマリキーを取得できる。そのプライマリキーのインデックスファイルを読み込むとクラスタードインデックスなのでそこからB like '123%'なレコードを特定すれば速いんじゃないかな!」

ルールベースオプティマイザの限界

話は突然変わるのですが、貴方はとある結活サークルを運営しているとします。
参加している男性陣と女性陣とでそれぞれおつきあいしても良いという相手を投票してもらい、両想いの人を探すという企画を立てました。

テーブルでいうとこんな感じでしょうか。だいぶ適当ですけど。

create table men (
	id INT(11) NOT NULL AUTO_INCREMENT,
	woman_id INT(11),
	PRIMARY KEY (id)
);

create table women (
	id INT(11) NOT NULL AUTO_INCREMENT,
	man_id INT(11),
	PRIMARY KEY (id)
);

さてさて、貴方はお手伝いしてくれるスタッフに次のように伝えました

貴方「とりあえず男性会員のデータを全部見て、どの女性を好きかを調べてほしい。それから、その女性もその男性を好きかどうかをチェックしてくれる?」
お手伝いさん「はい。承知しました。」

さてさて、そのあと数時間ほどしてから戻ってくるとまだ作業をしているスタッフを発見しました。

貴方「おや?まだ終わらなそう?大変な作業をおしつけちゃったねぇ」
お手伝いさん「はい、女性会員は50人ぐらいなんですけど、男性会員は10,000人いるんです」
貴方「!!」

さて、上記のたとえ話ですけれども「女性会員がどの男性を好きかを先に調べればすぐに終わる作業」だったのに、という意図が伝わったでしょうか?
なぜそうなったのかというと、単純に作業指示者がこの「統計データ」を知らなかったので「より良い作業指示」を計画できなかったところです。

統計データを取得する事によってより最適な探索ルートを探す方法が考えだされまして、それがコストベースオプティマイザです。

コストベースオプティマイザ

コストベースオプティマイザはさまざまな統計を元にいくつかの処理方法を検討し、それらがどれぐらいコストのかかる作業なのかを計算してからどの処理方法を選ぶかを決定します。

ここでいう統計には色々な要素が含まれています。

  • テーブルの行数
  • ブロック数
  • ブロック数あたりの平均行数
  • 行の平均サイズ
  • 分布度
  • インデックスの深さ
  • CPU使用率
  • I/O

などなど。いろいろな要素がありますが、この中でも最も大事なのは分布だと思います。

先ほどの例に戻る

さきほどの例に出たクエリです。

select * from TABLE
where A = '10' and B like '123%';

次のような情報を得ていた場合、あなたはどちらのインデックスを使いますか?

  • 実はAという列は10='男', 20='女'という2種類の値のみで構成されている平均分布度が50%の列です。
  • Bは0〜9999の値で0.01%の平均分布です。

この場合はBのインデックスが効率が良さそうです。

コストベースオプティマイザのほうが優れているが

上記の例からもわかるようにコストベースオプティマイザのほうが優秀だと思います。
ただしコストベースオプティマイザのほうが優秀と考えるならば統計情報にも感心を持つ必要があります。

とりあえず以上をまとめる

ルールベースオプティマイザとコストベースオプティマイザはともに目的とするデータを探索する経路を決定するための考え方です。

例えば10000人の中から次の条件を満たすバスケ選手を探してスカウトしたい場合を考えます。

1.バスケ歴3年以上であること
2.身長185cm以上であること

この際、「バスケ暦3年以上である 」ことを優先するのか「身長185cm以上である」ことを優先するのかを最初にルールとして定めるシンプルなやり方がルールベースオプティマイザです。

対して身長185cm以上の人が少なく、バスケ歴3年以上の人は多いという予測でできたなら身長185cm以上の人から先に絞り込み、そこからバスケ歴3年以上の人を探す
方法を採用するのがコストベースオプティマイザです。ただし、この方法には分布度といった統計など必要になります。

こんなので大丈夫かな?

というわけで

オプティマイザは絶対ではないので場合によっては開発者が探索経路をコントロールしてあげましょう。
以下、うまくコントロールしてあげている例です。わかりやすい良い記事ですね。

http://blog.nomadscafe.jp/2011/08/coverting-index-self-join-mysql.html