時計を壊せ

駆け出してからそこそこ経ったWebプログラマーの雑記

Time::Strptime進捗

頭を悩ませながらのんびり作ってます。
https://github.com/karupanerura/Time-Strptime

Time::Strptimeとは

strptimeのpure perlによる実装です。
epochとoffsetだけを返すシンプルな機能を持っています。
pure perlですが最小構成のperl codeを文字列として構築してevalする事により条件分岐の回数などを極限まで減らし高速な動作を実現しています。

最近の変更

timezoneサポートを実装しました

具体的には%Z/%zを環境依存*1でparse出来るようになりました。
また、この変更に伴い、offsetを返すようになりました。parseした文字列のtimezone/offset、あるいは現在のtimezoneからoffsetを計算し、返します。
offsetの計算にはTime::TZOffsetが利用出きるケースではTime::TZOffsetを利用し、そうでないケースではTime::Localを利用しています。

localeサポートを実装しました

具体的には%b/%aがparse出来るようになりました。
Encode::LocaleとPOSIX::strftimeを使って動的に組み立てるというカッコイイことをしています。*2

ベンチマークスクリプトを更新しました

https://github.com/karupanerura/Time-Strptime/blob/master/author/benchmark.pl
Time::Pieceのインスタンスをキャッシュしてstrptimeするケースと、キャッシュしないケースを分けました。
テストも同時に実行しており、同じ結果を返すケースでのベンチマークである事がちゃんと分かるようになっています。
GMT/UTCにおいて、Time::Pieceのインスタンスをキャッシュしないケースより45%程度高速というベンチマーク結果となっております。


tpがTime::Piece、dtがDateTime、tsがTime::Strptime(本モジュール)です。

    # Subtest: GMT(+0000)
    ok 1
    ok 2
    1..2
ok 1 - GMT(+0000)
Benchmark: timing 100000 iterations of dt, dt(cached), tp, tp(cached), ts, ts(cached)...
        dt: 47 wallclock secs (46.43 usr +  0.09 sys = 46.52 CPU) @ 2149.61/s (n=100000)
dt(cached): 27 wallclock secs (27.22 usr +  0.05 sys = 27.27 CPU) @ 3667.03/s (n=100000)
        tp:  2 wallclock secs ( 1.62 usr +  0.00 sys =  1.62 CPU) @ 61728.40/s (n=100000)
tp(cached):  1 wallclock secs ( 0.96 usr +  0.01 sys =  0.97 CPU) @ 103092.78/s (n=100000)
        ts: 34 wallclock secs (34.09 usr +  0.11 sys = 34.20 CPU) @ 2923.98/s (n=100000)
ts(cached):  1 wallclock secs ( 1.12 usr +  0.00 sys =  1.12 CPU) @ 89285.71/s (n=100000)
               Rate       dt       ts dt(cached)        tp ts(cached) tp(cached)
dt           2150/s       --     -26%       -41%      -97%       -98%       -98%
ts           2924/s      36%       --       -20%      -95%       -97%       -97%
dt(cached)   3667/s      71%      25%         --      -94%       -96%       -96%
tp          61728/s    2772%    2011%      1583%        --       -31%       -40%
ts(cached)  89286/s    4054%    2954%      2335%       45%         --       -13%
tp(cached) 103093/s    4696%    3426%      2711%       67%        15%         --
    # Subtest: UTC(+0000)
    ok 1
    ok 2
    1..2
ok 2 - UTC(+0000)
Benchmark: timing 100000 iterations of dt, dt(cached), tp, tp(cached), ts, ts(cached)...
        dt: 47 wallclock secs (46.49 usr +  0.08 sys = 46.57 CPU) @ 2147.31/s (n=100000)
dt(cached): 27 wallclock secs (26.62 usr +  0.05 sys = 26.67 CPU) @ 3749.53/s (n=100000)
        tp:  1 wallclock secs ( 1.64 usr +  0.00 sys =  1.64 CPU) @ 60975.61/s (n=100000)
tp(cached):  1 wallclock secs ( 0.81 usr +  0.00 sys =  0.81 CPU) @ 123456.79/s (n=100000)
        ts: 34 wallclock secs (33.53 usr +  0.11 sys = 33.64 CPU) @ 2972.65/s (n=100000)
ts(cached):  1 wallclock secs ( 1.13 usr +  0.00 sys =  1.13 CPU) @ 88495.58/s (n=100000)
               Rate       dt       ts dt(cached)        tp ts(cached) tp(cached)
dt           2147/s       --     -28%       -43%      -96%       -98%       -98%
ts           2973/s      38%       --       -21%      -95%       -97%       -98%
dt(cached)   3750/s      75%      26%         --      -94%       -96%       -97%
tp          60976/s    2740%    1951%      1526%        --       -31%       -51%
ts(cached)  88496/s    4021%    2877%      2260%       45%         --       -28%
tp(cached) 123457/s    5649%    4053%      3193%      102%        40%         --
    # Subtest: Asia/Tokyo(+0900)
    ok 1
    ok 2
    1..2
ok 3 - Asia/Tokyo(+0900)
Benchmark: timing 100000 iterations of dt, dt(cached), tp, tp(cached), ts, ts(cached)...
        dt: 55 wallclock secs (54.70 usr +  0.11 sys = 54.81 CPU) @ 1824.48/s (n=100000)
dt(cached): 34 wallclock secs (33.92 usr +  0.06 sys = 33.98 CPU) @ 2942.91/s (n=100000)
        tp:  2 wallclock secs ( 1.61 usr +  0.01 sys =  1.62 CPU) @ 61728.40/s (n=100000)
tp(cached):  1 wallclock secs ( 0.79 usr +  0.00 sys =  0.79 CPU) @ 126582.28/s (n=100000)
        ts: 39 wallclock secs (39.50 usr +  0.13 sys = 39.63 CPU) @ 2523.34/s (n=100000)
ts(cached):  2 wallclock secs ( 1.79 usr +  0.01 sys =  1.80 CPU) @ 55555.56/s (n=100000)
               Rate       dt       ts dt(cached) ts(cached)        tp tp(cached)
dt           1824/s       --     -28%       -38%       -97%      -97%       -99%
ts           2523/s      38%       --       -14%       -95%      -96%       -98%
dt(cached)   2943/s      61%      17%         --       -95%      -95%       -98%
ts(cached)  55556/s    2945%    2102%      1788%         --      -10%       -56%
tp          61728/s    3283%    2346%      1998%        11%        --       -51%
tp(cached) 126582/s    6838%    4916%      4201%       128%      105%         --
    # Subtest: America/Whitehorse(-0700)
    ok 1
    ok 2
    1..2
ok 4 - America/Whitehorse(-0700)
Benchmark: timing 100000 iterations of dt, dt(cached), tp, tp(cached), ts, ts(cached)...
        dt: 57 wallclock secs (56.63 usr +  0.11 sys = 56.74 CPU) @ 1762.43/s (n=100000)
dt(cached): 36 wallclock secs (35.81 usr +  0.06 sys = 35.87 CPU) @ 2787.84/s (n=100000)
        tp:  2 wallclock secs ( 1.67 usr +  0.01 sys =  1.68 CPU) @ 59523.81/s (n=100000)
tp(cached):  1 wallclock secs ( 0.80 usr +  0.00 sys =  0.80 CPU) @ 125000.00/s (n=100000)
        ts: 40 wallclock secs (40.52 usr +  0.14 sys = 40.66 CPU) @ 2459.42/s (n=100000)
ts(cached):  3 wallclock secs ( 2.06 usr +  0.01 sys =  2.07 CPU) @ 48309.18/s (n=100000)
               Rate       dt       ts dt(cached) ts(cached)        tp tp(cached)
dt           1762/s       --     -28%       -37%       -96%      -97%       -99%
ts           2459/s      40%       --       -12%       -95%      -96%       -98%
dt(cached)   2788/s      58%      13%         --       -94%      -95%       -98%
ts(cached)  48309/s    2641%    1864%      1633%         --      -19%       -61%
tp          59524/s    3277%    2320%      2035%        23%        --       -52%
tp(cached) 125000/s    6992%    4982%      4384%       159%      110%         --
1..4

TODO

リファクタリング

スキマ時間でえいやと実装を進めてしまいがちで、カオスになってきたので整理する。

テストケースの追加

なんか微妙なバグを拾いきれてないきがするのでテストを整える。

サポートするべきフォーマットの選定/実装

他の実装のstrptimeを見てみて必要があらば実装する。

ドキュメンテーション

サポートしているformatの説明と形式、localeの変更のしかたなどを説明する。

リリース

https://github.com/chansen/p5-time-moment/issues/4#issuecomment-36947533
有益っぽいのではやくリリースしたい。

XSによる高速化

Time::Localの実行が遅いので互換制のあるインターフェースでXS版を書いてみる。

XS実装の作成

libcのstrptimeだと%z/%Zがparse出来ないので、結構頑張る必要がある。(たぶん作らない)

[追記] timezone処理の環境依存を切る

http://www.iana.org/time-zones のあたりからいい感じにモジュール化して別のpackageで出してそれに依存させる。

*1:/usr/share/localeなどに依存

*2:やめたい

state変数は初期化のタイミングで例外が発生しても二度と初期化が行われる事は無い

タイトルの通り。
まあ、初期化は1度しか行われないので、あたりまえといえばあたりまえですがハマったのでメモ。


以下検証コード。undef,2,2となるかと思いきや初期化は1度しか行われないのでundef,undef,undefとなる。

static variable be undef if throw when initialize ...

#yapcasia 2013の感想とParallel::Asyncを書いたはなし

前夜祭

前夜祭では昨年のLTソンと同じくらいの熱気が会場を沸かせ、ときにはApache::LogFormat::CompileとかNorikuraとか真面目なプロダクトの紹介もありつつ、Acme系のはなしなどいろいろあっておもしろかったです。
ぼくも熱気に当てられて飛び込みLTとかやりました。ネタのつもりだったけど滑って残念な感じですみませんでした。

なお、スライドでも触れてる通り、ポチポチゲーの印象がつよいソシャゲですが、
最近はパズドラなどゲームゲームしているものも多く、遊ぶのも開発するのも面白いものが多いですので、
この機会に遊んでみては如何でしょうか。
中で触れてる奴についてはごにょごにょしてる最中ですの気になる方はで続報をお待ち頂ければと思います。

1日目

自分の発表まで資料の見直しと圧縮やらを繰り返しつつトークを聴いていました。

@rjbs さんのトークはPerl5.18あたりで入った新機能や廃止されるかもしれない機能、Perl開発の実態についての紹介でした。ガンガンいい感じの機能いれてこうぜって感じのアツいトークだった気がします。英語勉強しようと思いました。


id:songmu さんのDBまわりのはなしは、(たぶん)同業種かつ同職種である故か、知ってるところはうんうんうんとすごい勢いで同意したり、しらなかったところは id:kfly8 と「これやろうぜ!!」って盛り上がったりしました。


トークが終わった頃には次に聞こうと思っていた id:myfinder さんのRiakのはなしは既に始まっていて、
会場に着いたころには結構話がすすんでしまっていたきがします。
ぼくはRiakについて殆ど知らなかったので、あとで調べて発表のYoutube聴き直しながら勉強しようとおもいました。


お昼は @python_spameggs さんと麺場ハマトラに行きました。熱々濃厚で美味しかったです。


午後は id:yappo さんのメンテナンスしやすいコードを保つためのtool chainの工夫みたいなところで、
現状のsetupスクリプトの課題と、その解決策としてKsgkを作ったという感じの話でした。
だいぶテンションあがりました。


このあとぼくの発表でした。

緊張しててカバン背負ったまま壇上に立ち、それに気付いて頭が真っ白になって、アレな感じでした。
詳細は http://karupanerura.hateblo.jp/entry/2013/09/21/164133 をどうぞ。
Youtube見なおしたらやっぱりだいぶ酷くで申し訳なさでいっぱいであります。。。
次こそはと思っているので、来年の運営には少しでも力になれればと思っております。


そのあとは気ままにトーク聴いたりBOF行ったりぶらぶらしてました。
id:ry_mizuki がSpicaという素晴らしいモジュール書いてたのでLTでいいから発表しようぜとpushしたら発表してくれました!


Clientライブラリが無い、BtoB向けのAPIやマイナーなサービスのAPIを叩くときとかに重宝すると思います。
万能ではないけどだいたいのケースに対応出来るのでいいかんじです。
CPANにあがるのを

懇親会は去年以上にいろいろな人とおはなし出来て楽しかったし、料理も酒も美味しくて素晴らしかったです!
懇親会スポンサーの:DeNAさんありがとうございました!

懇親会のあとはHubに行ってなんか呑みながらくっちゃべってたきがしますが気が抜けすぎて酔っ払ってあんまり覚えていません。


あと家帰っていろいろ考えて反省とかしてたきがする。

2日目


こんなかんじでした

Hackathon


日曜日は用事があり公式のHackathonには行けなかったので月曜日にひとりHackathonをやりました。
最近、仕事でJavaを書く機会があり、Javaはカジュアルに並列処理を書けるので、
Perlでもカジュアルに並列処理できたらおもしろそうだなと思ってつくりました。

こんなかんじで並列処理が書けます。waitとか考えてなさそうなかんじ、カジュアルです。

use Parallel::Async;

my $task1 = async {
    my $res = ...;
    return $res;
};
my $task2 = async {
    my $res = ...;
    return $res;
};
my $task3 = async {
    my $res = ...;
    return $res;
};

my ($res1, $res2, $res3) = $task1->join($task2)->join($task3)->recv();

単純にforkすることやdaemonizeする事もメソッド1つ呼び出せば出来るようなかんじになっております。
isucon予選では即興でのworker代わりに使おうとしましたが時間がありませんでした。*1
けっこういい感じだと思いますので良かったらお使いください。

最後に

素晴らしいイベントをいままで育て、主導して運営してきた id:lestrrat さん、 id:941 さん、スタッフのみなさん、ありがとうございました。
本文中にもちらっと書いておりますが、来年の開催、運営を微力でもお手伝いできればと考えております。
コード書く事くらいしか取り柄がありませんがお力添えできれば幸いです。

*1:本当はResqueとかq4mとか使ったほうがよいはず。去年は時間切れで活きませんでしたがCluntchを使いました。

#yapcasia 2013 で 「ぼくがかんがえたさいきょうのMVC」について話してきました

昨日の13:40から多目的教室3で「ぼくがかんがえたさいきょうのMVC」という題でトークをさせて頂きました。


http://yapcasia.org/2013/talk/show/f60b8522-d43e-11e2-ac80-4cc16aeab6a4

内容について

大それたタイトルですが、MVC disというかんじではなく、上手くMVCを使うためにそれぞれのアプリケーションの要件に最適化した「ぼくがかんがえたさいきょうのMVC」をつくり、そこに対して規約を作り運用していくとだいぶ良い感じですよという感じの話で話をさせて頂きました。
20分は思いの外短いなということで、トピックスに対する答えはだいたい詰め込んだつもりですが、
時間の都合上一部削ったものもあります。すみません。


前半でMVCを何故カスタマイズするべきかについて触れ、後半でMVCをカスタマイズする例*1とそれを守るためにチーム内規約を作って運用するのオススメですという感じの事を話しました。
MVCは抽象的、って表現はいま思えば適当ではなかったです。「MVCは必ずしもあなたの書こうとしているアプリケーションの構成要素に対して具体的な紐付けが確定されているものではない」って表現のがたぶん僕の言いたい事に近いです。
なので具体的な構成要素との紐付けは自分で考えなければいけないけど、チーム内で解釈にブレが出るとカオスになるし、ちょっとイレギュラーな事をやってるとどこに紐付ければ良いか分からなくなるので、具体的に目的のアプリケーションのどの部分とMVCそれぞれが紐づくのか具体化しましょうということが伝えたかったです。


今回、MVC自体がビギナー向けの話題ではないかもしれないと思い、レギュラー向けで応募させて頂きましたが、ビギナーの方にも分かりやすい内容にまとめられたかなと思います。スライドはぜひご覧ください。内容についておかしいと思う点、不明な点があれば、Twitterのメンションやコメント欄などでぜひ気軽に質問/反論してください。



という問題があったみたいで、Sub Room3で id:yappo さんのトークを聴いていたのですが、Sub Room2での発表だと思ってちょっと前に抜けてSub Room2に行ったら違かったので慌ててSub Room3に戻ってだいぶ緊張した状態でトークを始める感じになるというハプニングもありました。
※これは僕が事前にちゃんとトークの前後関係や部屋を把握していれば防げた問題で、僕の責任です。アプリ自体は非常に便利に利用させて頂きました。作者の方、本当にありがとうございます。

*1:実例をもとに簡略化しました

YAPC::Asia 2013 で ぼくがかんがえたさいきょうのMVC についてはなします #yapcasia

YAPC::Asiaとは

日本で最大級の規模のPerlのカンファレンスです。Perlのカンファレンスですが運用、プロトコル、テスト等開発に関わる様々なトークがあり、和気あいあいとした雰囲気でときには意識高い話もしつつ、ゆるふわにPerlとその周辺技術等への学びや発見を得る事が出来ます。詳しくは http://yapcasia.org/2013/ をどうぞ。

今回、ありがたい事に僕が応募していたトークを採択して頂きました。

ぼくがかんがえたさいきょうのMVCとは

下のリンク先をご覧下さい。

http://yapcasia.org/talk/show/f60b8522-d43e-11e2-ac80-4cc16aeab6a4

ではちょっとさみしいのでこのトークを応募した経緯を書いてみます。


僕は仕事でソーシャルげーむを作っています。ソーシャルゲームは若干特殊な部分はありますが基本的には普通のWebアプリです。(最近はクライアントをAndroid/iOS向けに提供しているものも多いですがそれも多くの場合はバックエンドはWebAPIを提供するWebアプリです)
ソーシャルゲームは場合によってはかなりの種類の機能を提供する場合があり、対応デバイスもAndroid4系/Android2系、iOS/FeaturePhone(所謂ガラケー)などに対応させ、更に複数のソーシャルプラットフォームで動作させる場合もあり、プラットフォームによってOpenSocialの同じAPIでも微妙に仕様が違う場合があったり、同じ目的のAPIをそれぞれ独自の仕様で持っている場合があり、またゲーム自体のロジックが複雑な場合もあり、規模が大きくなる傾向があります。
ある程度の規模まではぶっちゃけ設計が適当でもなんとかなります。意味のわからないコードもひたすら追えばなんとかならなくもないでしょう。最悪全部書き直す事が現実的なコストでできる場合もあります。場合によってはschemaだけ真面目に設計してコードはゴリっと愚直に書いたほうが運用を含めた総合的なコストを低くする事もできる場合も多いと思っています。
しかし、ある程度以上になってくると、適当な設計のアプリケーションでは全てのコードを把握する難易度はかなり上がります。特にRPG系と呼ばれるジャンルのソーシャルアプリは確実に困る規模だと思います。
規模が大きいとどうしても複数人で同時に開発を進める事になります。規模が大きい割に設計が適当だと、同じ役割のコードが様々な場所に書かれたり、様々な機能が沢山搭載されている「ぼくがかんがえたさいきょうのClass!!!」「ぼくがかんがえたさいきょうのmethod!!!」みたいなやつが出来たり、非常にカオスなコードが出来上がります。処理の共通化も困難になり、そのようなコードベース上で無理に行われた共通化は諸悪の根源になりがちです。プロダクションコードでこのようなコードが出来上がると非常に悲しいことになります。テストが書かれていればかなりマシですが、それでもそもそもの設計がぐちゃぐちゃだとリファクタリングにかなり苦労する印象が強いです。コードレビューも苦労します。

これを解決するための考え方の1つが僕はソフトウェアアーキテクチャだと考えています。MVCはそのうちの1つですが、これも必ずしもそのままWebアプリケーションに当てはめられる訳ではなく、特にRDBMSとの関わり方やコンテンツキャッシュとの関わり方について悩んだり、ロジックが変に分散してしまったり、逆に殆どのロジックが詰め込まれる「ぼくがかんがえたさいきょうのClass」みたいな奴が出来て苦労する場合が多いと思います。その為、MVCの考え方を拡張して、目的のアプリケーションに合わせて整理して設計する事が必要になります。
そこに関してある程度考えがまとまったので、今回この話をする事にしました。
ただ、ぼくの考えも非の打ち所の無い完璧なものであるとは思っておらず、より良い感がえ方がきっとあると思うので、この話をきっかけにみんなそれぞれの「ぼくがかんがえたさいきょうのMVC」について話し合うきっかけにでもなれば、ベースの設計の重要性について考える機会が出来たらいいな風な感じです。
id:TAKESAKO さんの「SPDY、HTTP/2.0の使い方」とかid:xtetsujiさんの「mod_perlの展望とApacheの超絶技巧」とかid:akiymさんの「Herokuで学ぶ、初めてのPerl」とか裏番組みんな面白そうなので僕も聴きに行きたいくらいですが、もし興味があれば聴きに来て頂けると喜びます!

YAPC::Asiaに参加するには

チケットの購入が必要です。2日通し券で5000円、1日券で4000円です。なお、学生は無料です。(!!)
チケットの購入はこちらからどうぞ! http://yapcasia.org/2013/tickets/
明日、8/11までなのでまだ購入していない方はお早めに!

Yomiuri構想

まだ構想段階だけどこれだいぶイケてね?って思ったので勢いで書いてみる。

Yomiuriとは

手軽に拡張出来るJekyllインスパイア系ブログフレームワーク/オーサリングツール

特徴

CLIで記事が書ける

vimemacsなどお好みのエディタでどうぞ。

Github Flavord Markdownで記事が書ける

エンジニア的にアツい。

レイアウトの自由度

Kolon(Text::Xslate)の継承テンプレートで良い感じに拡張出来ます。
また、良い感じのメソッドを提供しているので
まるまるテンプレートを置き換える事もできます。
JS/CSSも自由に書けます。

機能拡張の自由度

JavaScriptとWebAPIを使って機能拡張を行う事をYomiuriはサポートします。
用意されたhook pointにJavaScriptAPIクライアント処理を埋め込むと良い感じにWebAPIを叩いてunderscore.jsで書いたtemplateをレンダリングするイメージです。
仕様として決まるのはWebAPIのCSRF対策*1JavaScriptのhook pointだけです。*2
この仕様を満たす事が出来ればWebAPIは自由な言語で実装する事が出来ます。
また、APIを楽に実装するための標準API仕様とサーバーサイド(Perl)/クライアントサイド(JavaScript)フレームワークも別途用意します。
大概はこれをつかうと楽にそこそこ便利なAPIをおてがるに開発出来る様になる予定です。

シンプルな動作

Jekillと同様に記事ファイル(Markdown)とテンプレートから静的なHTMLを生成するだけです。
gitで管理しやすいと思います。
静的ファイルサーバーがあれば公開可能なのでやろうと思えばGithub Pagesとかでも公開出来ます。*3

使い方イメージ

インストール

$ cpanm App::Yomiuri

新しいブログを作成

対話式で良い感じに設定作ってくれる。

$ yomiuri create MyBlog
title: My Blog of darkness
description: publishing my darkness
creating yomiuri.toml ... OK
creating MyBlog ... OK
$ cd MyBlog
$ git init
$ git add .
$ git commit -m 'initialize commit'

暗黒面を綴るブログが出来ました。

カテゴリの追加

カテゴリははてブと同様にタグっぽいやつ。

$ yomiuri category add darkness
$ yomiuri category add emacs

darknessとemacsという2つのカテゴリが出来ました。

記事の作成/編集

insert or update的な感じで処理してくれるのでファイル食わせるだけです。
h1(レベル1の見出し)の前に[]でカテゴリを囲んで書くとそのカテゴリの一覧に追加してくれます。

$ cat > entry/hoge.md
[darkness][emacs]My first entry.
===============

Hi.

I write a blog post for the first time. 
My hobby is to messing about with emacs.
^D
$ yomiuri entry entry/hoge.md

HTMLなどの生成

コマンド一発でお手軽生成

$ yomiuri publish
creating htdocs/index.html ... OK
creating htdocs/entry/hoge.html ... OK
creating htdocs/archive/1.html ... OK
creating htdocs/config.json ... OK
creating htdocs/meta.json ... OK
生っぽい感じで拡張
$ cat > htdocs/js/darkness.js
(function () {
  var Comment = {
    model: null,
    view:  null
  };

  Comment.Model = Backbone.Model.extend({
    apiUrl: "http://api.yomiuri.yourhost.com/comment/",
    list  : function () { return this.get("list"); }
  });
  Comment.View = Backbone.View.extend({
    el     : "#yomiuri-comment",
    model  : new Comment.Model(),
    events : {
      "click .button.add":    "openAddEditModal",
      "click .button.res":    "openAddEditModalWithRes",
      "click .button.submit": "submit"
    },
    initialize: function() {
      Comment.model.get(this.$el.attr)
      this.template      = _.template($("script#yomiuri-comment-template").text());
      this.modalTemplate = _.template($("script#yomiuri-comment-modal-template").text());
      this.render();
    },
    openAddEditModal: function (e) {
      $("#yomiuri-comment-modal").html(this.modalTemplate());
      $("#yomiuri-comment-modal").modal("show");
    },
    openAddEditModalWithRes: function (e) {
      var targetCommentId = e.target.getAttribute("data-comment-id");
      $("#yomiuri-comment-modal").html(this.modalTemplate({ targetCommentId: targetCommentId }));
      $("#yomiuri-comment-modal").modal("show");
    },
    submit: function() { throw "飽きた"; },
    render: function() {
      this.$el.html(this.template({ list: this.model.list }));
    }
  });

  Yomiuri.router.on("entry/:name", function () {
    new Comment.View();
  });
});

})();
^D

本日の一言

Jekillでよくね!!!!!!という気持ちは凄く分かります。
ぶっちゃけ最初はPerlでJekill的な奴自作してみよっかなー的なところから始まって、
JS自由に書いて機能拡張する事考えたらある程度フレームワークあると楽だよねってなって、
ああいう感じの静的HTML吐く系のブログエンジンを利用者が独自拡張するためのアプローチとして、
こういう感じの事が『手軽に』出来たら楽しいんじゃないかなと思っている。
そのためには仕様と実装をサポートするライブラリの整備が不可欠なので、
また、その肝心の部分はまだまじめに作り始めてないので実装が進んだらまた記事書きます。
まとまんねーーーーーーーーーーーーーー

本日のこだわりぽいんと

宗教間戦争が起きないようにcatで書きました

*1:http://d.hatena.ne.jp/hasegawayosuke/20130302/p1

*2:なんか足りない気もしつつ実装段階でまた考え直す

*3:Github Pagesに公開するのはgitのrootがそのままページのrootになるという仕様上ちょっと面倒なのでなんか上手い手段を考え中…

コンテキストとハッシュの怪

怖い話

割とハマりがちなので。
たとえば、以下のようなコードがあったとします。

use strict;
use warnings;
use utf8;

use Data::Dumper;

sub yyy {
    my $key = shift;
    return unless $key;

    return "yyy_$key";
}

my $hashref = {
    xxx => yyy(),
    aaa => 'bbb',
    ccc => 'ddd',
};

print Dumper $hashref;

上のサンプルコードの出力は以下のようになります。

$VAR1 = {
'bbb' => 'ccc',
'xxx' => 'aaa',
'ddd' => undef
};

マテマテマテマテって思うかもしれませんが、perlのバグなどではなく正常な挙動です。

コンテキスト

Perlにはコンテキストと呼ばれる概念があります。
これは、値の解釈によって処理を変えるための概念です。
詳しい説明は初めてのPerl*1を読むか、こことかこことかを読むと良いかもしれません。

Perlは値をスカラーコンテキストで評価する場合とリストコンテキストで評価する場合があり、それぞれで処理内容が変わる場合があります。
例えば変数への代入の場合はスカラー型の変数への代入の場合は値をスカラーコンテキストで評価します。

例1:

use strict;
use warnings;
use utf8;

my $calar1 = 'aaa';                 ## スカラーをスカラーコンテキストで評価
my $calar2 = ('xxx', 'yyy', 'zzz'); ## リストをスカラーコンテキストで評価(リストの最後の値が得られる)

print $calar1, "\n"; # 'aaa'
print $calar2, "\n"; # 'zzz'

アレイ型/ハッシュ型変数への代入の場合は値をリストコンテキストで評価します。
例2:

use strict;
use warnings;
use utf8;

use Data::Dumper;

# ARRAY
my @rray1 = 'aaa';                 ## スカラーをリストコンテキストで評価(与えたスカラー値が1つだけ入ったリストが得られる)
my @rray2 = ('xxx', 'yyy', 'zzz'); ## リストをリストコンテキストで評価

# HASH
my %ash1  = 'aaa';                 ## スカラーをリストコンテキストで評価(与えたスカラー値がkeyとなりvalueがundefなハッシュが得られる)
my %ash2  = ('xxx', 'yyy', 'zzz'); ## リストをリストコンテキストで評価

print Dumper \@rray1; # ['aaa']
print Dumper \@rray2; # ['xxx', 'yyy', 'zzz']

print Dumper \%ash1; # { 'aaa' => undef }
print Dumper \%ash2; # { 'xxx' => 'yyy', 'zzz' => undef }

また、更に変な書き方として、リストの中にリストを含む事も出来ます。
これは結合され1つのリストとして解釈されます。

('xxx', 'yyy', ('aaa', 'bbb')); # => ('xxx', 'yyy', 'aaa', 'bbb')

ハッシュ内では値はリストコンテキストで評価します。
なので、一番上の何も返さない関数の場合、空のリストとして解釈されてキーがズレてしまうのです。

{
    xxx => (),
    aaa => 'bbb',
    ccc => 'ddd',
}; ## { xxx => 'aaa', bbb => 'ccc', ddd => undef }

この場合、ほぼプログラマの思い通りの挙動にはならないでしょう。怖いですね。

こわいのこわいのとんでけー

上のコード類を実行してみた人は以下のような警告が出るのに気づいたかもしれません。

Odd number of elements in hash assignment

これはハッシュとして与えたリストが奇数だった場合に警告してくれています。
上のような間違ったコードを書いてしまっている場合がある事を危惧して(かどうかは知りませんが)教えてくれているのです。
なので警告は/dev/nullには決して捨てたりせずに*2、不可解な挙動が起こった場合は警告も含め怪しいメッセージが出ていないか確認するとよいでしょう。

*1:最近第6版が発売されたようですね

*2:捨てると椅子が飛んできます