Rubyはハッシュに数値と文字列のkeyが持てるんですね
Rubyで書かれたコードにbugがあって調べることになり、初めて知りました。
Ruby
% ruby -e 'h = {1 => true, "1" => false}; p h' {1=>true, "1"=>false}
Perl
% perl -MData::Dumper -e '$h = {1 => true, "1" => false}; warn Dumper $h' $VAR1 = { '1' => 'false' };
PHP
% php -r '$h = array(1 => true, "1" => false); var_dump($h);' array(1) { [1]=> bool(false) }
追記
@lesamoureuses @chisei :-)メンション消してしもたので リファレンスへのポインタを再度かいときますURLの要約部分"キーには任意の種類のオブジェクトを用いることができますが〜"の下りでした。
ということで、
- Object#hash ハッシュの格納に用いられるハッシュ値の計算
- Object#eql? キーの同一性判定
の2つを定義すれば何でもkeyになるよという話。
ハッシュの特性を考えれば確かにそうですよねそうですよね。
Perlとバーコードリーダーで本棚整理をするよ! #vgadvent2012
こんにちはこんにちは、VOYAGE GROUPエンジニアblog Advent Calendar 17日めを担当するmonmonといいます!
どんなネタにしようか土曜日まで迷ったあげく、せっかくの会社のAdvent Calendarなので会社に関係する話にしました!
どんな話をするの?
会社の1FにOASISという図書室がありまして、その本の整理をしたいなぁと思いました(あくまで今回のネタ用なので実際はやらないです)。
なので、今回はその話をしようと思います。
ちなみに上の展開写真がOASISね。
何でこの話にしたの?
理由は3つありまして、
- 前職の先輩からバーコードリーダーをもらったが使う機会なくて物置に眠らせてた
- PerlのジョブキューシステムであるQudoを使ってみたかった
- うちの会社の@TonnyXuが昔SQLiteのFTS(全文検索)について話してくれたことを思い出した
という感じです。
仕事でもそうだけど、何かを作り出すときにこういう部品をつなぎ合わせることで形になっていくってのは楽しいものですね。
あぁ!そういえばジョブズも「役に立つ可能性があるとは思わなかった点と点が、10年後に繋がって素晴らしいものができた」みたいな良い話をしてましたね!それだ!
処理の流れ
さぁ!ということで処理の流れだ!最終的に以下のような感じになったよ!
- バーコードリーダーでバーコードを読み取り、ISBN13をqueueに入れる
- queueからISBN13を取り出しAmazon Product Advertising APIで検索
- あればさらにY!の日本語形態素解析APIで形態素解析
- それらをSQLiteに突っ込む
- あとは全文検索でうまー
これをひとつずつ見ていこう!
ジョブキューを使う
ということで次。
ISBNを読み込んだ後、その検索結果を待たずに次のISBNを読み込みたいためジョブキューを使います。
で、今年のYAPC::AsiaでもQudoの話があったし、なのでQudoを使ってみようかなと調べたんですが、
調べているうちにこの辺の記事で今回の用途ならJonkでいいかもと思い変更しました。
Jonkの使い方は
https://metacpan.org/module/Jonk
と
https://metacpan.org/module/Jonk::Cookbook::Basic
に書いてあり、これをまるっとコピーしてきたらサクッと動くくらい楽で、ほぼそのままコピペです。ありがたいありがたい。
2年前のPerl Advent Calendarでも取り上げられているのでもっと難しいことやるときはこっちも参考にしようかなと思ってました(が、時間なくてここまでいかず)。
具体的なコードはこんな感じ。while 1で無限ループ作ってひたすらISBNをキューに突っ込むだけです。
#!perl # script/jonk-client.pl use strict; use warnings; use FindBin; use DBI; use Jonk::Client; use lib "$FindBin::Bin/../lib"; use MyApp; my $setting = MyApp->new(appdir => "$FindBin::Bin/../")->setting; my $dbh = DBI->connect("dbi:SQLite:$setting->{jonk}->{db}",'',''); my $jonk = Jonk::Client->new($dbh); while (1) { print 'input ISBN: '; next if <STDIN> !~ m/(\d+)/xms; my $isbn = $1; my $job_id = $jonk->enqueue('Book', $isbn); print "enqueued job_id: $job_id\n"; }
で、取り出して仕事する方はこんな感じ。実際のロジックはMyApp::Worker::Bookにあるので、そっちを呼び出しているだけですね。
#!perl # script/jonk-worker.pl use strict; use warnings; use FindBin; use DBI; use Jonk::Worker; use lib "$FindBin::Bin/../lib"; use MyApp; use MyApp::Worker::Book; my $setting = MyApp->new(appdir => "$FindBin::Bin/../")->setting; my $dbh = DBI->connect("dbi:SQLite:$setting->{jonk}->{db}",'',''); my $jonk = Jonk::Worker->new($dbh => {functions => [qw/Book/]}); my $worker = MyApp::Worker::Book->new( appdir => "$FindBin::Bin/../", setting => $setting, ); while (1) { if (my $job = $jonk->dequeue) { warn "work job: $job->{arg}\n"; $worker->work($job); } else { sleep(3); # wait for 3 sec. } }
ここで呼び出されたWorkerが
- Amazon Product Advertising APIを使う
- 形態素解析APIを使う
- DBに保存する
の3つの処理を行います
Amazon Product Advertising APIを使う
これは超簡単。Amazonのサイトで自分のkeyとsecret、それにassociate_tagの3つを取得した後、
CPANにあるURI::Amazon::APAを使えば良いです。
弾さんのブログに使い方の記事もあるのでハマる所はないですね。
唯一のハマりどころは日本のURLである「http://ecs.amazonaws.jp/onca/xml」の方を使うということかしら。
コピペするだけで本の検索ができちゃうなんて素敵ですね!
形態素解析APIを使う
さて、つづいて形態素解析です。
これは何で必要になったかと言うと、FTSを使うものの、タイトルなどを一綴りのまま入れてしまうとヒットしないパターンがあるのでそれをどうにかしたかったから。
以下のブログも参考にしました。
http://blog.mwsoft.jp/article/34911345.html
なので、「MeCabを入れて…」と考えたんですが、「今回そんなにガッツリやる必要ないのでもっと楽なのがいいなぁ」と方向修正。
結果、Y!の日本語形態素解析APIです。
で、「これもCPANにあるんじゃないかなぁ」と思ったらやっぱりあったよ!
https://metacpan.org/module/WebService::YahooJapan::WebMA
ただしURLが古いので以下のように変更して使います。
$WebService::YahooJapan::WebMA::APIBase = 'http://jlp.yahooapis.jp/MAService/V1/parse';
具体的なコードはこんな感じ。ほぼSYNOPSISのままですが、word_listの要素が1つの場合に配列になっていなかったのでそこだけ対応しました。
my $api = WebService::YahooJapan::WebMA->new( appid => $self->appid, ); my $res = $api->parse( sentence => $sentence, ress => 'ma', response => 'surface,reading,base form' ) or croak $api->error; my @word_list = (ref $res->{ma_result}->{word_list} eq 'ARRAY') ? @{$res->{ma_result}->{word_list}} : ($res->{ma_result}->{word_list}); $self->_to_string($self->_flatten(@word_list));
コレを使って、「タイトル」「作者」「出版社」の3つを形態素解析し、それらをspase区切りの文字にしてDBに突っ込むようにしてます。
具体例をあげるとPerl CPANモジュールガイドの場合には、
「Perl CPAN もじゅーる モジュール ガイド がいど つみた 冨田 尚樹 なおき わーくす こーぽれーしょん ワークス コーポレーション」のような文字列がDBに突っ込まれるわけです。
ふむ!すごいですね!これで「perl」でも「cpan」でも「モジュール」でも「冨田」でも検索にかかるようになりました!それだけじゃなく「もじゅーる」みたいな読みでもかかるようになりました!そして、なんと「つみた」でもかかります!すごい!
SQLite FTSを使う
さてさて、最後にDBについて。
今回の本データをためるDBですが、今まで話に出て来た通りSQLiteのFTS4を使いました
(ファイルだから開発も楽だしね!)
FTSの使い方は簡単で、いつものcreate tableの文を以下のようにするだけです。
CREATE VIRTUAL TABLE book USING fts4( isbn, asin, title, author, manufacturer, words );
「CREATE VIRTUAL TABLE」になっているところと、「USING fts4」になっているところが違う所で、他は特に変わりません。
ただ、UNIQUE INDEX利かななくなっちゃうため、アプリ側でユニーク確認をするようにしました。
(もしかしたら何かやり方あるのかもだけど調べきれず。Stack Overflowにはtriggerとかでどうにかする例があったけどそこまでの話ではなかったので深く見てないです)
SQLの発行方法は「match」というキーワードを使うだけ。
また、SQLite FTS3 and FTS4 Extensionsにあるように「where table名 match 'hogehoge'」というようにテーブル名を指定すればカラムをまたいで探してくれます。
具体的なqueryは以下のような感じ。
SELECT * FROM book WHERE book MATCH 'perl cpan';
DEMO的なやつ
以上でできあがった仕組みがこちらです。
https://github.com/monmon/vgadvent2012-app
それでは、写真で使っている様子を見て行きます。
1. まずjonk-client.plを起動するとISBNの入力待ちになります。
2. あとはひたすらバーコードリーダーでISBNを読みまくります。入力するとすぐに「早く次のISBNくれよぉ!」状態になるのでひたすら入れまくります。
3. で、一方違うターミナルでjson-worker.plを起動させておけば、以下のようにキューに入ったISBNを取り出し、一連の処理を始めます。
4. 結果、SQLiteに本データがたまるので、あとは全文検索で検索してうまーです。
5. 「タイトル」と「作者」の両方にあるような「単語」も検索できて便利ですね!!!!
まとめ
初めは「iPhoneでガンガンscanしてブクログに突っ込んで終わり」みたいなネタを考えたんですが、いざバーコードリーダーでやってみるとISBN読み込んでqueueに突っ込むだけだからよりガンガンscanできて楽しかったです。
(ちょうど大そうじの季節だったし、このネタにして良かった!)
まぁエラー処理とか全然考えてないし、既に「原作」と「漫画」のように2人の作者がいる場合にはauthorカラムに「ARRAY(0x7f8f6ac2fe30)」のように突っ込まれちゃうbugが判明したけど気にしません。
作ってみないとわからないことあるしね!リーン何ちゃらだ!
ということで、「コピペばっかりでも簡単にここまで作れるよ、でもコピペばっかりだと全然考慮が足りないから仕事ではちゃんと理解して物を作っていこう」という話でした!
明日は誕生日が終わったばかりの@tadasyさんです!今からでも間に合うのでみんなmention飛ばして祝ってみてください!
Rである母集団のdata.frameから除きたい集団data.frameがあって、その結果が欲しいとき
母集団がこんな感じで
population <- data.frame(id=c(1, 2, 3), data=c('a', 'b', 'c')) id data 1 1 a 2 2 b 3 3 c
除きたいのがこんな感じのとき
except <- data.frame(id=c(2), data=c('b')) id data 1 2 b
で、こんな感じで!とinを使えばいいです
population[!(population$id %in% except$id),,drop=FALSE] id data 1 1 a 3 3 c
覚えておいた方がいいのは「drop=FALSE」の部分で、これがないと1列(ベクトル)の時に結果の次元が落ちてしまう(idってのが欲しいのに1 3が返ってくる)
> population <- data.frame(id=c(1, 2, 3)) > except <- data.frame(id=c(2)) > population[!(population$id %in% except$id),] [1] 1 3
1つめのカンマの前で「どういう行を出すか」、2つめのカラムの前で「どういう列を出すか」(今回の場合は何もないので全て)、その後にoptionだと思えば良い
Rubyで2つの日付を元にからその間の月を全部出す
パッとググった感じこんなんでよさそう
#!ruby require 'date' (Date.parse("2010-08-01")..Date.parse("2012-11-07")).each{|i| next if i.strftime('%d') != '01' puts i.strftime('%Y%m') }
参考:rubyなら日付を連続10日分の日付作るのがアツ−!と言う間です - それマグで!
追記
何かeachの中でnextっておかしいね。returnの方がいいんだろうか
Rで日付を出す。月の連番を出す。
RでSQL作るときとかに今月から半年分くらいの月が欲しかったりするんだけど、そんなときはSys.Date()使えばいい。
> paste(format(seq(Sys.Date(), length.out=6, by="-1 month"), "%Y%m"), collapse=", ") [1] "201210, 201209, 201208, 201207, 201206, 201205"
- 現在の日付を取って、
- そこから-1ヶ月ずつ6個作り、
- formatを%Y%mにして、
- ", "で連結して文字列化
いろんな会社のいろんな人の話を聞けるのはやっぱり楽しい!YAPC::Asia Tokyo 2012に参加して来た!
こんにちは!こんにちは!monmonといいます!
2009年に初めて参加して、一昨年、去年に引き続き今年もスタッフとして参加させてもらいました。
3年続けてスタッフ参加だったためメインホールのリーダー担当を任されたんですが、今回は初めての場所ということもあって失敗しないか心配で仕方なかったです。
と、失敗してない風なこと言いましたが、2日目におもいきりやらかしました。
「一番長いのが40分と思い込んでた」+「東大の学食に行ったら長蛇の列で帰って来たのが始まる5分前だった」の結果、
Perl 今昔物語のときに勘違いして10分前カンペ出してしまい、馮さんが941さんに「あれ?60分でしたよね?」と聞かなくてはいけないはめに。
モデレータが何分枠しらないとかまじひどいわw #yapcasia
すみません、僕のカンペのせいですみません。
慣れない学食なんて行くもんじゃないなってのと、ちゃんと時間は確認しないとダメだと心に刻みました。
ただ、こういう失敗はあったものの、スタッフ全体の動きはスムーズでした。
何かあればLINEでやり取りをして解決していたため、以前のようにバタバタすることがなかったなぁと思います。
# 去年はfacebookメッセージでやり取りをしたんですが、LINEの方がみんなに合っていたからなのか、去年よりもやり取りが活発でした。
会場の準備や片付けについても、牧さん941さんをはじめとする実行委員の人たちのノウハウとスタッフの数の多さのおかげでホントにビックリするくらいな順調具合。最後も「本当にもう終わっちゃうの?!」という感じであっという間に終わってしまい「でかいイベントなのに手際良くてすごいなぁすごいなぁ」という第三者目線で感動してました。
準備のワンシーンとして恒例となったZIGOROuメソッド(いわゆるバケツリレー)でのノベルティ詰めも貼っておきます。
この流れ作業、近くの人とワイワイ話すきっかけにもなってスタッフ間の交流に一役かっているんじゃないだろうかと感じてます。
スタッフ間でのぼっち対策ですね。
そうそう、話が前後しますが、今回僕は少し早めに着いたので他の人が来る間あんちぽさんのSQALEプロマイドを一人で堪能してました。
これはその夜の風景。
あんちぽさんに「どれが当たりですか?」とみんなで聞いているところです。
# ちなみに当たりは草原でかわいいポーズ取っているやつ。
# Ricoさんがコンプさせてたので参考:https://twitter.com/RicoImazu/status/251875701408550912
話がそれた。
去年も一昨年も思ったんですが、941さんのイベントを回す動きはすごいですね。準備の仕方、指示の出し方、問題回避ととっさの対応、業者さんへの直接ダメ出し、と、イベントをやるような人にとっては勉強になることたくさんあるだろうなぁと改めて思いました。
さて、スタッフの話はこの辺にして。
今回はメインホール担当だったのでトークもいくつか聞くことができました。
僕の印象に残っているトークはtypesterさんの「Perlハッカーは息をするようにCPANモジュールを書く。」です。
既にスライドは上がっていますが、
「そのモジュールを作った熱い想いをドキュメントに書こう!」
の流れはスライドではなく動画で観た方がいいので動画が上がったら観てみてください。
このトークの中に出てくる「ドキュメントは熱い想いをぶつけるところ」って表現がとても好きです。
僕は仕事をしているときに「こういうやり方もできたんだけど、〜って理由があったからこのやり方でやったんだ」というのを知るとだいぶ仕事がやり易くなります。
作った人のポリシーみたいなものが見えていると、それだけで「なんでここはこうなの?」的な面倒なやり取りがだいぶ減ってコストが下がるんです。
そういう相手の場合、その人が作る物がどういうものなのかがだんだんわかってきて、ますます仕事がやりやすくなり、そしてもう何て言うかとても楽なんです。なんでですかね、癖みたいな物がわかるってことなんですかね。
なので「ドキュメントに熱い想い」ってのはいい表現だなぁと。
とてもいい表現なので会社でも使いたいなと思っています。
もう一つ印象に残っているトークは最後のmizzyさんの話。
話を聞きながら
mizzyさんの話いいなぁ。「今までこういうことやってきました。だからこうやるといいと思ってる」話はとても理解しやすいし説得力あるし良いお父さんそう #yapcasia
ってtweetもしたし、社内の日報にも「新人の子たちは動画観るといいよ」って感想を書きました。
去年のhidekさんの話もそうだけど、YAPCに来るといろんな会社のいろんな人の思いが聞けて楽しいです。
飲みに行ったりすれば聞けるんだろうけど、こんなにいっぺんにいろんな人の話が聞ける機会ってやっぱりそうそうないから、「どういう考え方でコードを書いている」「どういう考え方でサービスを作っている」「どういう考え方で行動している」みたいな話が聞けるのは貴重だなと感じています。
しかも一方通行で聞くだけではなく、質問すれば聞きたいことがもっと聞ける。とても楽しいです。
スタッフも楽しいけど話す側も楽しそうだから次回はそっちもやろうと思います。
次回のYAPC::Asiaも楽しみです!
最後に、今回も牧さん941さん長い期間かけての準備おつかれさまでした!ありがとうございました!
他のbranchの内容を調べる時はgit grepしてgit showする
「前に書いたmethodがなくなってるなぁ」
「あぁ、あれ違うbranchで作ったmethodか」
みたいなことを調べる時にタイトルのようにやればいいみたい。
まずはgit grepで違うbranchから探したいものを検索
(git)-[hotfix/2.4.20]-[12:30]% git grep isUniqueWith feature/m48 [/Applications/MAMP/htdocs/lib] feature/m48:app/Model/Enticement.php: 'isUniqueWith' => array( feature/m48:app/Model/Enticement.php: 'rule' => array('isUniqueWith', 'company'), feature/m48:app/Model/Enticement.php: public function isUniqueWith($data, $fields)